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

Posted

技术标签:

【中文标题】在 Node.js 中使用 POST 请求上传文件【英文标题】:Uploading file using POST request in Node.js 【发布时间】:2014-10-10 06:41:31 【问题描述】:

我时遇到问题。我必须使用request 模块来实现这一点(没有外部 npms)。服务器需要它是包含文件数据的file 字段的多部分请求。在不使用任何外部模块的情况下,在 Node.js 中看起来很容易却很难做到。

我尝试过使用this example,但没有成功:

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

【问题讨论】:

你的表格有enctype="multipart/form-data"选项吗? 我没有使用任何形式。是服务器请求。我正在使用套接字将文件从浏览器发送到服务器,然后我必须使用 POST 请求将该文件发送到另一台服务器。 【参考方案1】:

看起来您已经在使用request module。

在这种情况下,您只需使用form feature 发布form feature:

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 module,request 在内部使用。

【讨论】:

在学习node和request模块的时候,我很困惑为什么在调用post方法后可以修改表单。埋在request 文档中的是解释 - 形式“可以修改,直到在事件循环的下一个周期触发请求”。 我在使用 form 和 form.append 时不断收到“[Error: write after end]”,有人知道为什么吗? @VitorFreitas 你应该调用req.form() 并在调用request.post 后立即用所有适当的数据同步填充它。在同一个事件循环滴答期间执行此操作很重要,否则您的请求可能已经发送并且底层流关闭。 request 已弃用,您有替代方案吗? @David got 是一个不错的选择【参考方案2】:

request 实现的 formData 字段的一个未记录功能是能够将选项传递给它使用的 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 选项。

This change 是在 2014 年 10 月添加的(所以在提出这个问题 2 个月后),所以现在应该可以安全使用(在 2017 年+)。这相当于requestv2.46.0 或更高版本。

【讨论】:

【参考方案3】:

Leonid Beschastny 的答案有效,但我还必须将 ArrayBuffer 转换为 Node 的 request 模块中使用的 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
);

【讨论】:

有一种更简单的方法可以将ArrayBuffer 转换为Buffer,使用内置的Bufferconstructor from an array of octets:var buffer = new Buffer(new Uint8Array(ab)); 你上一个函数中file.data、file.name和file.type中的“文件”是从哪里来的?我没有在其他任何地方看到该变量。 我正在使用 Meteor 和社区包进行文件管理。但是,如果您使用的是纯节点,那么您可以使用文件系统函数来获取有关文件及其数据的所有信息nodejs.org/api/fs.html【参考方案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);
    
  );

【讨论】:

请edit 回答您的问题,并添加一些关于您的代码如何工作的解释或评论。这将帮助其他用户确定您的答案是否足够有趣以供考虑。否则人们必须分析你的代码(这需要时间),甚至模糊地知道这是否是他们需要的。谢谢! 5 年后有人会想要一个解释,而你不会在身边或不会打扰。这就是为什么 Fabio 要求您在回复中而不是应要求提供解释。【参考方案5】:

我是这样做的:

// Open file as a readable stream
const fileStream = fs.createReadStream('./my-file.ext');

const form = new FormData();
// Pass file stream directly to form
form.append('my file', fileStream, 'my-file.ext');

【讨论】:

【参考方案6】:
 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);

【讨论】:

以上是关于在 Node.js 中使用 POST 请求上传文件的主要内容,如果未能解决你的问题,请参考以下文章

Node js HTTP 服务器/静态 Servlet POST 请求

从 Electron 应用程序发出多部分 POST 请求以进行文件上传

如何在 node.js 中处理 POST 请求

如何使用普通的POST请求将文件发送到apps脚本?

关于Node.js的httpClieint请求报错ECONNRESET的原因和解决措施

Node.js GET/POST请求