在 JavaScript 中为文件上传生成 HTTP 多部分正文

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25455475/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 04:32:10  来源:igfitidea点击:

Generating HTTP multipart body for file upload in JavaScript

javascriptfilehttpmeteorhttp-headers

提问by ?ukasz Jagodziński

I'm trying to build HTTP multipart form data in JavaScript (on the server for Meteor.js HTTP request).

我正在尝试在 JavaScript 中构建 HTTP 多部分表单数据(在 Meteor.js HTTP 请求的服务器上)。

Here is the Meteor code that sends POST request.

这是发送 POST 请求的 Meteor 代码。

var res = HTTP.post(url, {
  headers: formatted.headers,
  content: formatted.content
});

I'm preparing headers and content using this code.

我正在使用此代码准备标题和内容。

function MultipartFormData(parts) {
  var boundary = (new Date()).getTime();
  var bodyParts = [];

  _.each(parts, function (value, key) {
    value.data = (new Buffer(value.data)).toString('binary');

    bodyParts.push(
      '---------------------------' + boundary,
      'Content-Disposition: form-data; name="' + key + '"; filename="' + value.filename + '"',
      'Content-Type: ' + value.contentType,
      '',
      value.data);
  });

  bodyParts.push('---------------------------' + boundary + '--', '');

  var bodyString = bodyParts.join('\r\n');

  return {
    content: bodyString,
    headers: {
      'Content-Type': 'multipart/form-data; boundary=' + '---------------------------' + boundary,
      'Content-Length': bodyString.length
    }
  }
}

Details about file:

有关文件的详细信息:

key = 'file'
value.filename = 'file.png'
value.contentType = 'image/png'
value.data is an Uint8Array

Server is unable to process this request. When I'm using standard Node.js request object and FormBuilder using the same data everything work properly. I'm just requesting http connection between two servers. Could anyone tell me what is wrong with my code? I'm not the expert in HTTP protocol and I have only shreds of information about generating HTTP request's content.

服务器无法处理此请求。当我使用标准 Node.js 请求对象和 FormBuilder 使用相同的数据时,一切正常。我只是请求两台服务器之间的 http 连接。谁能告诉我我的代码有什么问题?我不是 HTTP 协议方面的专家,我只有关于生成 HTTP 请求内容的一些信息。

And one more thing. I've tried converting Uint8Array to Buffer, ArrayBuffer, String and it also didn't work.

还有一件事情。我试过将 Uint8Array 转换为 Buffer、ArrayBuffer、String,但也没有奏效。

EDIT:

编辑:

I've made test what is the http body when sending the same file in Firefox and in my app:

在 Firefox 和我的应用程序中发送相同的文件时,我已经测试了 http 正文是什么:

Firefox:

火狐:

Content-Type: multipart/form-data; boundary=---------------------------19039834425958857471335503868
Content-Length: 299

-----------------------------19039834425958857471335503868
Content-Disposition: form-data; name="file"; filename="test.png"
Content-Type: image/png

PNG


IHDR

·ü]tPLTE?a  7
IDAT×càúq?IEND?B`
-----------------------------19039834425958857471335503868--

My App:

我的应用程序:

Content-Type: multipart/form-data; boundary=---------------------------1408816255735
Content-Length: 263

---------------------------1408816255735
Content-Disposition: form-data; name="file"; filename="test.png"
Content-Type: image/png

PNG


IHDR

·ü]tPLTE?a      7
IDA×cà
                                 úq?IEND?B`
---------------------------1408816255735--

It differs a little bit but I don't know what is the source of this difference.

它有点不同,但我不知道这种差异的根源是什么。

EDIT 2

编辑 2

And the server response is: Error: failed [400] Invalid multipart request with 0 mime parts.

服务器响应是: Error: failed [400] Invalid multipart request with 0 mime parts.

EDIT 3

编辑 3

When generating body like this:

当生成这样的身体时:

Content-Type: multipart/form-data; boundary=1408827490794
Content-Length: 213

--1408827490794
Content-Disposition: form-data; name="file"; filename="test.png"
Content-Type: image/png

PNG


IHDR

·ü]tPLTE?a  7
IDA×cà
      úq?IEND?B`
--1408827490794--

I get the error: Error: failed [400] Missing end boundary in multipart body.

我收到错误: Error: failed [400] Missing end boundary in multipart body.

采纳答案by Tomasz Gawel

Fix the boundary

修复边界

  1. If you define boundary as: BOUNDARY,
  2. then the beginning of each subpart is marked with: --BOUNDARY
  3. and the end of message is marked with: --BOUNDARY--
  1. 如果您将边界定义为:BOUNDARY,
  2. 然后每个子部分的开头都标有: --BOUNDARY
  3. 并且消息的结尾标有: --BOUNDARY--

Your boundary is defined as

你的边界被定义为

Content-Type: multipart/form-data; boundary=---------------------------1408816255735

So the boundary string is: ---------------------------1408816255735, which already has 28 -(If I counted properly), So the divider should be --+ the boundary string, (not "just the boundary string").

所以边界字符串是:---------------------------1408816255735,它已经有 28 个-(如果我计算得当),所以分隔符应该是--+ 边界字符串,(不仅仅是“边界字符串”)。

I would get rid of overhead ---------------------------as it obscures if this preceding --is added or not.

---------------------------如果--添加与否,我会消除开销,因为它会模糊不清。

Content-Type: multipart/form-data; boundary=1408816255735

...

...

--1408816255735
Content-Disposition: form-data; name="file"; filename="test.png"

...

...

--1408816255735--

The "Missing end boundary in multipart body" Error

“多部分正文中缺少结束边界”错误

As you say the problem after fixing boundary is: Missing end boundary in multipart body.

正如你所说的,固定边界后的问题是:Missing end boundary in multipart body.

You probably also need '\r\n' after ending bondary mark ;). As var bodyString = bodyParts.join('\r\n');does not put \r\n the after the last element, which I suppose is required. (Our discussion in comments showed that the this is not that case, so...)

您可能还需要 '\r\n' 结束绑定标记 ;)。Asvar bodyString = bodyParts.join('\r\n');没有把 \r\n 放在最后一个元素之后,我认为这是必需的。(我们在评论中的讨论表明情况并非如此,所以......)

...so I suspect that Content-Length is set incorectly (it should be the byte count not character's count in string). As Content-Length is not required - maybe try without it. (And this happened to be the right guess in this case).

...所以我怀疑 Content-Length 设置不正确(它应该是字节数而不是字符串中的字符数)。由于 Content-Length 不是必需的 - 也许尝试不使用它。(在这种情况下,这恰好是正确的猜测)。

Binary data

二进制数据

Ok until now we fixed mistakes in given code. As you say now the multipart request is parsed successfully but You get server error when it tries to interpret sent data as PNG image.

好的,直到现在我们修复了给定代码中的错误。正如您现在所说,多部分请求已成功解析,但是当它尝试将发送的数据解释为 PNG 图像时,您会收到服务器错误。

I do not know how you get binary data - there is no code for that. Assuming that in your loop over partd value.datais nodejs Buffer with image bytes I would try with:

我不知道你是如何获得二进制数据的——没有代码。假设在 partd 循环中value.data是带有图像字节的 nodejs 缓冲区,我会尝试使用:

_.each(parts, function (value, key) {
    bodyParts.push(
        '--' + boundary,
        'Content-Disposition: form-data; name="' + key + '"; filename="' + 
                value.filename + '"',
        'Content-Transfer-Encoding: base64',
        'Content-Type: ' + value.contentType,
        '',
        value.data.toString('base64'));
});

回答by ?ukasz Jagodziński

To reply @Jared Martin question I haven't been calculating content length. This solution only works with base64. If you would like to send bigger amount of data you would need to use binary format and compose response as a buffer.

要回答@Jared Martin 的问题,我还没有计算内容长度。此解决方案仅适用于 base64。如果你想发送更多的数据,你需要使用二进制格式并将响应作为缓冲区。

FormData = function () {
    this._parts = {};
};

FormData.prototype.append = function (name, part) {
    this._parts[name] = part;
};

FormData.prototype.generate = function () {
    var boundary = Date.now();
  var bodyParts = [];

  _.each(this._parts, function (part, name) {
    part.data = (new Buffer(part.data)).toString('base64');

    bodyParts.push(
      '--' + boundary,
      'Content-Disposition: form-data; name="' + name + '"; filename="' + part.filename + '"',
      'Content-Type: ' + part.contentType,
      'Content-Transfer-Encoding: base64',
      '',
      part.data);
  });

  bodyParts.push('--' + boundary + '--', '');

  return {
    headers: {
      'Content-Type': 'multipart/form-data; boundary=' + boundary,
    },
    body: bodyParts.join('\r\n')
  }
};