通过管道将多部分表单上载到另一个服务器

Pipe multipart form uploads to another server

本文关键字:上载 另一个 服务器 表单 管道 多部      更新时间:2023-09-26

我正试图在Node Express服务器上处理POST请求,以处理多部分表单上传,在我的情况下,用户正在上传图像。

我想通过我的Express应用程序将上传到另一台服务器,该应用程序目前被设置为使用body解析器,我也看到它不支持多部分bodes,而是建议使用其他一些库。

我见过多方参与,但我不确定如何在客户端应用程序中使用它。

在我的客户端代码中,我发布了一个FormData对象,如下所示:

function create(data, name) {
  var formData = new FormData();
  formData.append('file', data, name);
  return this.parentBase.one('photos').withHttpConfig({transformRequest: angular.identity}).customPOST(formData, undefined, undefined, {'Content-Type': undefined});
}

注意:我使用AngularJS的Restangular库,如所示

因此,根据我对多方文档的理解,我必须处理表单上传事件,并在表单上传完成后对其采取进一步行动。

问题是,我希望我可以直接通过管道上传到另一台服务器。之前,我的客户端应用程序直接调用另一台服务器,但我现在正试图通过Express路由所有内容,这可能吗,还是我必须使用类似多方的东西?

请求文档给出了一个使用formData的示例,但我不确定这将如何与我看到的多方示例一起使用。例如,一旦在Express中使用mutliparty完成上传,我是否必须构建另一个formData对象,然后用它发出进一步的请求,或者我是否必须将每个部分管道连接到另一个服务器?

我很困惑,有人能帮我澄清一下吗?

感谢

编辑

好吧,我看了一下下面@yarons评论中的multer,这似乎是我想要使用的东西,我已经尝试将其用于我的快速路由器设置,如下所示:

路由.js

var express = require('express'),
  router = express.Router(),
  customers = require('./customers.controller.js'),
  multer = require('multer'),
  upload = multer();
router.post('/customers/:customerId/photos/', upload.single('file'), customers.createPhoto);

controller.js

module.exports.createPhoto = function(req, res) {
  console.log(req.file);
  var options = prepareCustomersAPIHeaders(req);
  options.formData = req.file;
  request(options).pipe(res);
};

在上面的控制器中注销req.file属性我看到这个:

{ fieldname: 'file',
  originalname: '4da2e703044932e33b8ceec711c35582.jpg',
  encoding: '7bit',
  mimetype: 'image/png',
  buffer: <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 fa 00
 00 00 fa 08 06 00 00 00 88 ec 5a 3d 00 00 20 00 49 44 41 54 78 5e ac bd f9 8f e
6 e9 7a ... >,
  size: 105868 }

这就是我通过客户端代码发布的内容,使用:

var formData = new FormData();
      formData.append('file', data, name);
      return this.parentBase.one('photos').withHttpConfig({transformRequest: angular.identity}).customPOST(formData, undefined, undefined, {'Content-Type': undefined});

我所尝试的是明智的吗?只是它不起作用,我从我试图发布的服务器上收到了一个错误。在我直接向服务器发出发布请求之前,一切都很好,所以我的Express''Multer设置一定有问题

编辑2

好吧,经过更多的搜寻,我发现了这篇使用多方的文章,我有经理在我的设置中这样工作:

var request = require('request'),
  multiparty = require('multiparty'),
  FormData = require('form-data');
module.exports.createPhoto = function(req, res) {
  //console.log(req.file);
  var options = prepareCustomersAPIHeaders(req),
    form = new multiparty.Form();
  options.headers['Transfer-Encoding'] = 'chunked';
  form.on('part', function(part){
    if(part.filename) {
      var form = new FormData(), r;
      form.append(part.name, part, {filename: part.filename, contentType: part['content-type']});

      r = request(options, function(err, response, body){
        res.status(response.statusCode).send(body);
      });
      r._form = form
    }
  });
  form.on('error', function(error){
    console.log(error);
  });
  form.parse(req);
};  

这是现在为我上传文件,为我的另一个服务器预期,虽然这个解决方案工作,我不喜欢行:

r._form = form

似乎正在为请求对象分配一个私有表单变量,而且我在多方页面上看不到任何以这种方式记录的内容

有人能对这个可能的解决方案发表意见吗?

我们使用以下内容:

客户端

//HTML
    <input type="file" ng-file-select uploader="info.uploadPath" />

//DIRECTIVES
  // It is attached to <input type="file" /> element
  .directive('ngFileSelect', function() {
    return {
      link: function($scope, $element) {
        $element.bind('change', function() {
          $scope.$emit('file:add', this.files ? this.files : this);
        });
      }
    };
  })
//OTHER
    var uploadPath = '/api/things/' + $stateParams.thingId + '/add_photo'
    var uploadInfo = {
              headers: {
                'Authorization': authToken
              },
              form: {
                title: scope.info.name
              }
            }

//SERVICE:
  $rootScope.$on('file:add', function(event, items) {
    this.addToQueue(items);
  }.bind(this));
  ...
  addToQueue: function(items) {
    var length = this.queue.length;
    angular.forEach(items.length ? items : [items], function(item) {
      var isValid = !this.filters.length ? true : !!this.filters.filter(function(filter) {
        return filter.apply(this, [item]);
      }, this).length;
      if (isValid) {
        item = new Item({
          url: this.url,
          alias: this.alias,
          removeAfterUpload: this.removeAfterUpload,
          uploader: this,
          file: item
        });
        this.queue.push(item);
      }
    }, this);
    this.uploadAll();
  },
  getNotUploadedItems: function() {
    return this.queue.filter(function(item) {
      return !item.isUploaded;
    });
  },
  /**
   * Upload a item from the queue
   * @param {Item|Number} value
   */
  uploadItem: function(value, uploadInfo) {
    if (this.isUploading) {
      return;
    }
    var index = angular.isObject(value) ? this.getIndexOfItem(value) : value;
    var item = this.queue[index];
    var transport = item.file._form ? '_iframeTransport' : '_xhrTransport';
    this.isUploading = true;
    this[transport](item, uploadInfo);
  },
  uploadAll: function(uploadInfo) {
    var item = this.getNotUploadedItems()[0];
    this._uploadNext = !!item;
    this._uploadNext && this.uploadItem(item, uploadInfo);
  },
  _xhrTransport: function(item, uploadInfo) {
    var xhr = new XMLHttpRequest();
    var form = new FormData();
    var that = this;
    form.append(item.alias, item.file);
    angular.forEach(uploadInfo.form, function(value, name) {
      form.append(name, value);
    });
    xhr.upload.addEventListener('progress', function(event) {
      var progress = event.lengthComputable ? event.loaded * 100 / event.total : 0;
      that._scope.$emit('in:progress', item, Math.round(progress));
    }, false);
    xhr.addEventListener('load', function() {
      xhr.status === 200 && that._scope.$emit('in:success', xhr, item);
      xhr.status !== 200 && that._scope.$emit('in:error', xhr, item);
      that._scope.$emit('in:complete', xhr, item);
    }, false);
    xhr.addEventListener('error', function() {
      that._scope.$emit('in:error', xhr, item);
      that._scope.$emit('in:complete', xhr, item);
    }, false);
    xhr.addEventListener('abort', function() {
      that._scope.$emit('in:complete', xhr, item);
    }, false);
    this._scope.$emit('beforeupload', item);
    xhr.open('POST', item.url, true);
    angular.forEach(uploadInfo.headers, function(value, name) {
      xhr.setRequestHeader(name, value);
    });
    xhr.send(form);
  },

服务器

//things.router
app.route('/api/things/:thingId/add_photo')
  .post(things.uploadPhoto);
//things.controller
exports.uploadPhoto = function(req, res) {
  var formidable = require('formidable');
  var form = new formidable.IncomingForm();
  form.parse(req, function(err, fields, files) {
    var data = files.qqfile;
    //actual file is at data.path
    fs.createReadStream(data.path).pipe(request.put(uploadUrl));
  }
}