在Node.js中使用POST请求上传文件


76

我在Node.js中使用POST请求上传文件时遇到问题。我必须使用request模块来完成此操作(无需外部npms)。服务器需要它是file包含文件数据字段的多部分请求。看起来很容易,但不使用任何外部模块就很难在Node.js中完成。

我尝试使用此示例,但未成功:

request.post({
  uri: url,
  method: 'POST',
  multipart: [{
    body: '<FILE_DATA>'
  }]
}, function (err, resp, body) {
  if (err) {
    console.log('Error!');
  } else {
    console.log('URL: ' + body);
  }
});

1
您可以选择表格enctype="multipart/form-data"吗?
monkeyinsight 2014年

2
我没有使用任何形式。这是服务器请求。我正在使用套接字将文件从浏览器发送到服务器,后来我不得不使用POST请求将该文件发送到另一台服务器。
卢卡斯Jagodziński

Answers:


114

看起来您已经在使用requestmodule了

在这种情况下,您需要发布的multipart/form-data只是使用其form功能

var req = request.post(url, function (err, resp, body) {
  if (err) {
    console.log('Error!');
  } else {
    console.log('URL: ' + body);
  }
});
var form = req.form();
form.append('file', '<FILE_DATA>', {
  filename: 'myfile.txt',
  contentType: 'text/plain'
});

但是,如果您要从文件系统中发布一些现有文件,则可以简单地将其作为可读流传递:

form.append('file', fs.createReadStream(filepath));

request 将自行提取所有相关的元数据。

张贴的更多信息multipart/form-data请参阅node-form-data模块,它在内部使用request


83
当我学习节点和请求模块时,我对为什么在post调用该方法后可以修改表单感到困惑。埋在请求的文档是解释-形式“可以被修改,直到请求是在事件循环的下一个周期发射”。
Doug Donohoe 2015年

3
使用form和form.append时,我不断收到“ [错误:结束后写]”,有人知道为什么吗?
Vitor Freitas

1
@VitorFreitas,您应该在调用后立即调用req.form()并用所有适当的数据同步填充它request.post。在同一事件循环周期内执行此操作很重要,否则您的请求可能已经发送并且基础流已关闭。
Leonid Beschastny

@LeonidBeschastny可以看看我的代码吗? pastebin.com/E6b0cvag。在我看来不错,谢谢!requestService是请求模块
Vitor Freitas

1
请求已被弃用,您还有其他选择吗?
David

20

实现该formData字段的未记录功能request是能够将选项传递给form-data它使用的模块:

request({
  url: 'http://example.com',
  method: 'POST',
  formData: {
    'regularField': 'someValue',
    'regularFile': someFileStream,
    'customBufferFile': {
      value: fileBufferData,
      options: {
        filename: 'myfile.bin'
      }
    }
  }
}, handleResponse);

如果您需要避免调用requestObj.form()而需要将缓冲区作为文件上传,这将很有用。该form-data模块还接受contentType(MIME类型)和knownLength选项。

此更改是在2014年10月添加的(因此,在提出此问题后的2个月内),因此现在(2017年起)可以安全使用。等于v2.46.0或更高版本request


4

Leonid Beschastny的答案有效,但我还必须将ArrayBuffer转换为Noderequest模块中使用的Buffer 。将文件上传到服务器后,我使用了HTML5 FileAPI格式的文件(我正在使用Meteor)。下面的完整代码-可能对其他人有帮助。

function toBuffer(ab) {
  var buffer = new Buffer(ab.byteLength);
  var view = new Uint8Array(ab);
  for (var i = 0; i < buffer.length; ++i) {
    buffer[i] = view[i];
  }
  return buffer;
}

var req = request.post(url, function (err, resp, body) {
  if (err) {
    console.log('Error!');
  } else {
    console.log('URL: ' + body);
  }
});
var form = req.form();
form.append('file', toBuffer(file.data), {
  filename: file.name,
  contentType: file.type
});

4
使用八位字节数组中的内置构造函数,有一种更简单的方法可以转换ArrayBuffer为:BufferBuffer var buffer = new Buffer(new Uint8Array(ab));
Leonid Beschastny 2014年

2
在上一个函数中,file.data,file.name和file.type中的“文件”来自哪里?我在其他任何地方都看不到该变量。
michaelAdam15年

我正在使用Meteor和社区软件包进行文件管理。但是,如果您使用的是纯节点,则可以使用文件系统功能来获取有关文件及其数据的所有信息。nodejs.org
api/fs.html –ŁukaszJagodziński2015年

4

您还可以使用请求库中的“自定义选项”支持。这种格式允许您创建多部分表单上载,但同时具有文件和其他表单信息(如文件名或内容类型)的组合条目。我发现有些图书馆希望使用这种格式来接收文件上传,特别是诸如multer之类的图书馆。

该方法已正式记录在请求文档的表单部分-https: //github.com/request/request#forms

//toUpload is the name of the input file: <input type="file" name="toUpload">

let fileToUpload = req.file;

let formData = {
    toUpload: {
      value: fs.createReadStream(path.join(__dirname, '..', '..','upload', fileToUpload.filename)),
      options: {
        filename: fileToUpload.originalname,
        contentType: fileToUpload.mimeType
      }
    }
  };
let options = {
    url: url,
    method: 'POST',
    formData: formData
  }
request(options, function (err, resp, body) {
    if (err)
      cb(err);

    if (!err && resp.statusCode == 200) {
      cb(null, body);
    }
  });

4
编辑您的答案,并对代码的工作方式添加一些说明或评论。这将帮助其他用户确定您的答案是否足够有趣以至于无法考虑。否则,人们甚至不得不分析您的代码(这需要花费时间),甚至无法确定这是否是他们所需要的。谢谢!
Fabio说恢复莫妮卡的时间

5年后的今天,有人会想要一个解释,而您不会在周围或不会打扰。因此,Fabio要求您在回复中添加解释,而不是要求。
user985366 '17

0
 const remoteReq = request({
    method: 'POST',
    uri: 'http://host.com/api/upload',
    headers: {
      'Authorization': 'Bearer ' + req.query.token,
      'Content-Type': req.headers['content-type'] || 'multipart/form-data;'
    }
  })
  req.pipe(remoteReq);
  remoteReq.pipe(res);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.