如何从node.js上传文件?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (93)

如何将文件从浏览器上传到node.js服务器?我想从node.js代码上传文件到另一台服务器。我根据我有限的node.js知识来编写它却不起作用。

function (data) {
  var reqdata = 'file='+data;
  var request = http.request({
    host : HOST_NAME,
    port : HOST_PORT,
    path : PATH,
    method : 'POST',
    headers : {
      'Content-Type' : 'multipart/form-data',
      'Content-Length' : reqdata.length
    }
  }, function (response) {
      var data = '';
      response.on('data', function(chunk) {
        data += chunk.toString();
      });
      response.on('end', function() {
        console.log(data);
      });
    });

  request.write(reqdata+'\r\n\r\n');
  request.end();
})

上述功能由生成数据的其他代码调用。

我尝试使用curl -F“file = @ <filepath>”上载相同的数据文件,并且上载成功。但是我的代码失败了。服务器返回一个应用程序特定的错误,提示上传的文件无效/损坏。

我收集了tcpdump数据并用wireshark分析了它。从我的node.js代码发送的数据包缺少多部分数据所需的边界。我在wireshark数据包中看到此消息

The multipart dissector could not find the required boundary parameter.

如何在node.js代码中完成此操作?

提问于
用户回答回答于

Multipart非常复杂,如果你想让它看起来像客户端通常处理“multipart / form-data”,你必须做一些事情。你首先必须选择一个边界键,这通常是一个随机的字符串来标记这些部分的开始和结束,(在这种情况下,它只是一个部分,因为你想发送一个文件)。每个部分(或一个部分)将需要一个标题(由边界键初始化),设置内容类型,表单字段的名称和传输编码。

我从来没有使用multipart,但我认为这是如何做到的。

var boundaryKey = Math.random().toString(16); // random string
request.setHeader('Content-Type', 'multipart/form-data; boundary="'+boundaryKey+'"');
// the header for the one and only part (need to use CRLF here)
request.write( 
  '--' + boundaryKey + '\r\n'
  // use your file's mime type here, if known
  + 'Content-Type: application/octet-stream\r\n' 
  // "name" is the name of the form field
  // "filename" is the name of the original file
  + 'Content-Disposition: form-data; name="my_file"; filename="my_file.bin"\r\n'
  + 'Content-Transfer-Encoding: binary\r\n\r\n' 
);
fs.createReadStream('./my_file.bin', { bufferSize: 4 * 1024 })
  // set "end" to false in the options so .end() isnt called on the request
  .pipe(request, { end: false }) // maybe write directly to the socket here?
  .on('end', function() {
    // mark the end of the one and only part
    request.end('--' + boundaryKey + '--'); 
  });

再一次,我从来没有这样做过,但我认为这是如何实现的。也许更有知识的人可以提供更多的见解。

如果你想以base64或其他编码格式发送它,你必须自己做所有的管道工作。它最终会变得更加复杂,因为你将不得不暂停读取流并等待请求中的流失事件,以确保不会耗尽所有内存(如果它通常不是一个大文件不用担心这个问题),你可以在读取流选项中设置编码。

用户回答回答于

以下是适用于我们的修改版本:

var boundaryKey = Math.random().toString(16); // random string
request.setHeader('Content-Type', 'multipart/form-data; boundary="'+boundaryKey+'"');
// the header for the one and only part (need to use CRLF here)
request.write( 
  '--' + boundaryKey + '\r\n'
  // use your file's mime type here, if known
  + 'Content-Type: application/octet-stream\r\n' 
  // "name" is the name of the form field
  // "filename" is the name of the original file
  + 'Content-Disposition: form-data; name="my_file"; filename="my_file.bin"\r\n'
  + 'Content-Transfer-Encoding: binary\r\n\r\n' 
);
fs.createReadStream('./my_file.bin', { bufferSize: 4 * 1024 })
  .on('end', function() {
    // mark the end of the one and only part
    request.end('\r\n--' + boundaryKey + '--'); 
  })
  // set "end" to false in the options so .end() isn't called on the request
  .pipe(request, { end: false }) // maybe write directly to the socket here?

变化是:

  • ReadableStream.pipe 返回管道流,所以end永远不会被调用。相反,请等待end文件读取流。
  • request.end 把边界放在一个新的线上。

扫码关注云+社区

领取腾讯云代金券