一尘不染

如何从node.js上传文件

node.js

当我查询此问题时,发现了很多帖子,但是它们都涉及如何从浏览器将文件上传到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 = @
”上传相同的数据文件,并且上传成功。但是我的代码失败了。服务器返回一个特定于应用程序的错误,提示上载的文件无效/损坏。

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

The multipart dissector could not find the required boundary parameter.

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


阅读 332

收藏
2020-07-07

共1个答案

一尘不染

分段很复杂,如果要使其看起来像客户端通常如何处理“分段/表单数据”,则必须做一些事情。首先,您必须选择一个边界键,这通常是一个随机字符串来标记各个部分的开始和结束(在这种情况下,由于您要发送单个文件,因此它只是一个部分)。每个部分(或一个部分)都将需要一个标头(由边界键初始化),设置内容类型,表单字段的名称和传输编码。零件完成后,您需要使用边界键标记每个零件的末端。

我从来没有从事过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或原始二进制文件以外的其他编码形式发送它,则必须自己进行所有管道传递。最终将变得更加复杂,因为您将不得不暂停读取流并等待请求中的流失事件,以确保您不会耗尽所有内存(如果它不是一个大文件,通常不必为此担心)。
编辑: 实际上,没关系,您 可以 在读取流选项中设置编码。

如果还没有Node模块已经做到这一点,我会感到惊讶。也许有人对此主题有更深入的了解可以为您提供一些底层的细节方面的帮助,但是我认为应该在某个地方有一个模块来执行此操作。

2020-07-07