Javascript XMLHttpRequest 中表单数据的二进制字符串连接

Javascript binary string concatenation on form-data in XMLHttpRequest

我正在编写一个 Chrome 扩展,需要构建自定义表单数据以将 Zip 文件(我不能使用真正的 HTML 表单)上传到服务器(不是矿)。 我在这里找到了一种方法 - https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms/Sending_forms_through_JavaScript(最后一节 - 处理二进制数据)。

它可以很好地处理明文文件,但我想发送二进制的 Zip。

似乎 JavaScript 无法处理二进制和非二进制的连接,并在我的文件中添加了一些 0Xc2 字符)-:

我也在 http://footle.org/2007/07/31/binary-multipart-posts-in-javascript/ 找到了解决方案, 但它使用 Components.classes["@mozilla.org/io/string-input-stream;1"],这在 Chrome 扩展中不可用。

如何将二进制 zip 文件与字符串连接起来并将其上传到服务器?

function sendData(zipBinary) {

    var XHR      = new XMLHttpRequest();
    var boundary = "----WebKitFormBoundary3n9vu9ZOkCPW4HAw";
    var prefix     = "";
    var postfix     = "";

    prefix += "--" + boundary + "\r\n";
    prefix += 'content-disposition: form-data; '
          + 'name="'         + 'UploadedFile'          + '"; '
          + 'filename="'     + 'hello.zip' + '"\r\n';
    prefix += 'Content-Type: ' + 'application/x-zip-compressed' + '\r\n';
    prefix += '\r\n';
    postfix += '\r\n';

    // Once we are done, we "close" the body's request
    postfix += "--" + boundary + "--";
    postfix += '\r\n';

    XHR.addEventListener('load', function(event) {
      alert('Yeah! Data sent and response loaded.');
    });

    XHR.addEventListener('error', function(event) {
      alert('Oups! Something goes wrong.');
    });

    XHR.open('POST', 'https://example.com');
    XHR.setRequestHeader('Content-Type','multipart/form-data; boundary=' + boundary);
    XHR.send(prefix + zipBinary + postfix); // <----
  }

您是否阅读了所链接文章中的 section about FormData?因为这是通过 XMLHttpRequest API 发送文件的最佳解决方案。这种 API 优于字符串连接的优点是文件可以以流方式上传,而不必在发送前完全缓冲在内存中。

假设zipBinary是一个BlobFile对象,你可以上传一个文件如下:

function sendData(zipBinary) {
    var xhr = new XMLHttpRequest();
    var fd = new Formdata();
    fd.append('hello.zip', zipBinary);
    xhr.onload = function() {
         // Request completed! Use xhr.status and/or xhr.responseText to
         // check the server's response status code and response body.
    };
    xhr.onerror = function() {
         // Aw. Network error.
    };
    xhr.open('POST', 'https://example.com/');
    xhr.send(fd);
}

如果zipBinary不是Blob or File对象,而是一串二进制数据,那么您可以将其转换为具有指定MIME-type的Blob,如下所示:

function sendData(zipBinary) {
    var zipBinaryBytes = new Uint8Array(zipBinary.length);
    for (var i = 0; i < zipBinary.length; ++i) {
        zipBinaryBytes[i] = zipBinary.charCodeAt(i);
    }
    zipBinary = new Blob([zipBinaryBytes], { type: 'application/zip' });
    // rest of code as I suggested above...

(注意:application/zip 是 zip 文件的官方 MIME 类型,而不是 application/x-zip-compressed


如果您是 HTTP 协议的专家并且真的想要完全手写 HTTP 请求体,那么您需要发送一个类型化数组而不是字符串,因为 XMLHttpRequest API will encode a regular string as UTF-8 before passing off the data to the server. This can be done using the TextEncoder API since Chrome 38+,应用到你的代码如下:

// Was: XHR.send(prefix + zipBinary + postfix);
// New
XHR.send(new TextEncoder().encode(prefix + zipBinary + postfix));

注意:您很少需要手动构造请求正文。除非您知道自己在做什么,否则 必须手动构建请求主体通常表明您做错了