NodeJS / Restify:如何在API中接收文件上传? [英] NodeJS/Restify: How can I recieve file upload in API?

查看:547
本文介绍了NodeJS / Restify:如何在API中接收文件上传?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从移动应用程序上传图像文件(以本机编写,现在在iOS上运行)。

I'm trying to upload an image file from a mobile application (which is written in react native and now running on iOS).

文件发送到我的REST API,如下所示。我有两个问题:

The file is send to my REST API, which is shown below. I got two problems with that:


  1. 我没有得到 req.body ,因为它总是一个空对象,虽然标题是正确提交的。

  2. 我想通过 gridfs-stream将收到的文件写入我的数据库(GridFS)
  3. ,但我不明白该代码的放置位置。
  1. I do not get req.body, as it is always an empty object, although headers are submitted correctly.
  2. I want to write the recieved file to my DB (GridFS) via gridfs-stream, but I don't understand where to put that code.

API

const restify = require('restify')
const winston = require('winston')
const bunyanWinston = require('bunyan-winston-adapter')
const mongoose = require('mongoose')
const Grid = require('gridfs-stream')
const config = require('../config')

// Configure mongoose to work with javascript promises
mongoose.Promise = global.Promise

// Setting up server
const server = restify.createServer({
  name: config.name,
  version: config.version,
  log: bunyanWinston.createAdapter(log)
})

server.use(restify.plugins.multipartBodyParser())

server.listen(config.port, () => {
  mongoose.connection.on('open', (err) => {
    server.post('/upload', (req, res, next) => {
      console.log(req.headers) // <- returns headers as expected

      /* Problem 1 */
      console.log(req.body) // <- is empty object (unexpected)
      res.send(200, { message: 'successful upload' })
      res.end()
    })
  })

  global.db = mongoose.connect(config.db.uri, { useMongoClient: true })

  /* Problem 2: The recieved file should be stored to DB via `gridfs-stream` */
  // I think this is the wrong place for this line...
  var gfs = Grid(global.db, mongoose.mongo)
})

我试图找到错误,但我没有找到它,所以这里是数据,我在我的API中得到:

I tried to find the error, but I did not find it, so here is the data, which I get in my API:

标题

{ 
  host: 'localhost:3000',
  'content-type': 'multipart/form-data; boundary=pUqK6oKvY65OfhaQ3h01xWg0j4ajlanAA_e3MXVSna4F8kbg-zT0V3-PeJQm1QZ2ymcmUM',
  'user-agent': 'User/1 CFNetwork/808.2.16 Darwin/15.6.0',
  connection: 'keep-alive',
  accept: '*/*',
  'accept-language': 'en-us',
  'accept-encoding': 'gzip, deflate',
  'content-length': '315196' 
}

body

{ }

为什么 body 为空?

React Native文件上传

这就是我将文件发送到API的方式。我还向您展示了一些变量的内容:

This is how I am sending the file to the API. I also show you the content of some variables:

async function upload (photo) {
  console.log('photo', photo); // OUTPUT SHOWN BELOW
  if (photo.uri) {
    // Create the form data object
    var data = new FormData()
    data.append('picture', { uri: photo.uri, name: 'selfie.jpg', type: 'image/jpg' })

    // Create the config object for the POST
    const config = {
      method: 'POST',
      headers: {
        'Accept': 'application/json'
      },
      body: data
    }
    console.log('config', config); // OUTPUT SHOWN BELOW

    fetchProgress('http://localhost:3000/upload', {
      method: 'post',
      body: data
    }, (progressEvent) => {
      const progress = progressEvent.loaded / progressEvent.total
      console.log(progress)
    }).then((res) => console.log(res), (err) => console.log(err))
  }
}

const fetchProgress = (url, opts = {}, onProgress) => {
  console.log(url, opts)
  return new Promise((resolve, reject) => {
    var xhr = new XMLHttpRequest()
    xhr.open(opts.method || 'get', url)
    for (var k in opts.headers || {}) {
      xhr.setRequestHeader(k, opts.headers[k])
    }
    xhr.onload = e => resolve(e.target)
    xhr.onerror = reject
    if (xhr.upload && onProgress) {
      xhr.upload.onprogress = onProgress // event.loaded / event.total * 100 ; //event.lengthComputable
    }
    xhr.send(opts.body)
  })
}

photo

{
  fileSize: 314945,
  origURL: 'assets-library://asset/asset.JPG?id=106E99A1-4F6A-45A2-B320-B0AD4A8E8473&ext=JPG',
  longitude: -122.80317833333334,
  fileName: 'IMG_0001.JPG',
  height: 2848,
  width: 4288,
  latitude: 38.0374445,
  timestamp: '2011-03-13T00:17:25Z',
  isVertical: false,
  uri: 'file:///Users/User/Library/Developer/CoreSimulator/Devices/D3FEFFA8-7446-42AB-BC7E-B6EB88DDA840/data/Containers/Data/Application/17CE8C0A-B781-4E56-9347-857E74055119/Documents/images/69C2F27F-9EEE-4611-853E-FC7FF6E5C373.jpg'
}

config

'http://localhost:3000/upload', 
{ 
  method: 'post',
    body: 
    { 
      _parts: 
        [ 
          [ 'picture',
            { uri: 'file:///Users/User/Library/Developer/CoreSimulator/Devices/D3FEFFA8-7446-42AB-BC7E-B6EB88DDA840/data/Containers/Data/Application/17CE8C0A-B781-4E56-9347-857E74055119/Documents/images/69C2F27F-9EEE-4611-853E-FC7FF6E5C373.jpg',
              name: 'selfie.jpg',
              type: 'image/jpg' } 
          ] 
        ]
    }
}

我认为数据(应该在 config )格式错误。为什么数组中有数组?

I think data (which should be send as body in config) has wrong format. Why is there an array in an array?

推荐答案

以下示例使用 react-native-fetch-blob 在React Native部分,Nodejs使用Express和 Formidable 在服务器端解析表单。

The example below uses react-native-fetch-blob at React Native part, and Nodejs with Express and Formidable to parse form at the server side.

让我们在确定后首先上传文件用户是否上传了照片或视频:

Let's first upload the file after determining whether user uploaded a photo or video:

RNFetchBlob.fetch(
  'POST',
  Constants.UPLOAD_URL + '/upload',
  {
    'Content-Type': 'multipart/form-data'
  },
  [
    {
      name: this.state.photoURL ? 'image' : 'video',
      filename: 'avatar-foo.png',
      type: 'image/foo',
      data: RNFetchBlob.wrap(dataPath)
    },
    // elements without property `filename` will be sent as plain text
    { name: 'email', data: this.props.email },
    { name: 'title', data: this.state.text }
  ]
)
  // listen to upload progress event
  .uploadProgress((written, total) => {
    console.log('uploaded', written / total);
    this.setState({ uploadProgress: written / total });
  })
  // listen to download progress event
  .progress((received, total) => {
    console.log('progress', received / total);
  })
  .then(res => {
    console.log(res.data); // we have the response of the server
    this.props.navigation.goBack();
  })
  .catch(err => {
    console.log(err);
  });
};

同样,接收文件并相应地加载数据:

Similarly, receive file and load the data accordingly:

exports.upload = (req, res) => {
  var form = new formidable.IncomingForm();
  let data = {
    email: '',
    title: '',
    photoURL: '',
    videoURL: '',
  };

  // specify that we want to allow the user to upload multiple files in a single request
  form.multiples = true;
  // store all uploads in the /uploads directory
  form.uploadDir = path.join(__dirname, '../../uploads');

  form.on('file', (field, file) => {
    let suffix = field === 'image' ? '.png' : '.mp4';
    let timestamp = new Date().getTime().toString();

    fs.rename(file.path, path.join(form.uploadDir, timestamp + suffix)); //save file with timestamp.

    data[field === 'image' ? 'photoURL' : 'videoURL'] = timestamp + suffix;
  });
  form.on('field', (name, value) => {
    data[name] = value;
  });
  form.on('error', err => {
    console.log('An error has occured: \n ' + err);
  });
  form.on('end', () => {
    // now we have a data object with fields updated.
  });
  form.parse(req);
};

并使用控制器功能:

let route = express.Router();
// other controller functions...
route.post('/upload', uploadController.upload);
app.use(route);

请务必阅读代码中包含的注释。 Datapath 是使用 react-native-image-picker 。您可以使用 react-native-progress 来显示上传进度。

Make sure you read the comments included in the code. Datapath is media's path (not base64 string) created after using react-native-image-picker . You can use react-native-progress to show upload progress.

查看react-native-fetch-blob的multipartform-data部分以供进一步参考: https://github.com/wkh237/react-native-fetch-blob#multipartform-data-示例 - 表格后数据与文件和数据

Check out multipartform-data section of react-native-fetch-blob for further reference: https://github.com/wkh237/react-native-fetch-blob#multipartform-data-example-post-form-data-with-file-and-data

这篇关于NodeJS / Restify:如何在API中接收文件上传?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆