XMLHttpRequest:使用XML和图像作为有效负载的多部分/相关POST

oli*_*her 12 javascript picasa xmlhttprequest google-chrome-extension

我正在尝试从Chrome扩展程序中将图像(使用元数据)发布到Picasa Webalbums.请注意,正如我在此处所述,Content-Type image/xyz的常规帖子有效.但是,我希望包含描述/关键字,协议规范描述了带有XML和数据部分的多部分/相关格式.

我通过HTML5 FileReader和用户文件输入获取数据.我使用检索二进制字符串

FileReader.readAsBinaryString(file);
Run Code Online (Sandbox Code Playgroud)

一旦FileReader加载了字符串,假设这是我的回调代码:

function upload_to_album(binaryString, filetype, albumid) {

    var method = 'POST';
    var url = 'http://picasaweb.google.com/data/feed/api/user/default/albumid/' + albumid;
    var request = gen_multipart('Title', 'Description', binaryString, filetype);
    var xhr = new XMLHttpRequest();
    xhr.open(method, url, true);
    xhr.setRequestHeader("GData-Version", '3.0');
    xhr.setRequestHeader("Content-Type",  'multipart/related; boundary="END_OF_PART"');
    xhr.setRequestHeader("MIME-version", "1.0");
    // Add OAuth Token
    xhr.setRequestHeader("Authorization", oauth.getAuthorizationHeader(url, method, ''));
    xhr.onreadystatechange = function(data) {
        if (xhr.readyState == 4) {
            // .. handle response
        }
    };
    xhr.send(request);
}   
Run Code Online (Sandbox Code Playgroud)

gen_multipart函数只是从输入值和XML模板生成多部分,并生成与规范完全相同的输出(除了..binary图像数据..),但为了完整起见,这里是:

function gen_multipart(title, description, image, mimetype) {
    var multipart = ['Media multipart posting', "   \n", '--END_OF_PART', "\n",
    'Content-Type: application/atom+xml',"\n","\n",
    "<entry xmlns='http://www.w3.org/2005/Atom'>", '<title>', title, '</title>',
    '<summary>', description, '</summary>',
    '<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/photos/2007#photo" />',
    '</entry>', "\n", '--END_OF_PART', "\n",
    'Content-Type:', mimetype, "\n\n",
    image, "\n", '--END_OF_PART--'];
    return multipart.join("");
}
Run Code Online (Sandbox Code Playgroud)

问题是,POST有效负载与原始图像数据不同,因此导致错误请求(Picasa将不接受图像),尽管它在使用时工作正常

xhr.send(file) // With content-type set to file.type
Run Code Online (Sandbox Code Playgroud)

我的问题是,如何获得真正的二进制图像以将其包含在多部分中?我认为只是将它附加到xml字符串就会损坏它,但我似乎无法修复它.

请注意,由于Picasa中旧错误,base64不是解决方案.

Rob*_*b W 20

所述的XMLHttpRequest说明书指出该数据发送使用所述.send()方法转换为Unicode,并且编码为UTF-8.

上传二进制数据的推荐方法是通过FormDataAPI.但是,由于您不只是上传文件,而是将二进制数据包装在XML中,因此该选项无用.

该解决方案可以在FormData for Web Workers Polyfill的源代码中找到,我在遇到类似问题时编写了该代码.为了防止Unicode转换,所有数据都被添加到一个数组中,最后作为一个数据传输ArrayBuffer.根据规范,传输时不接触字节序列.

以下代码是基于Form Workers for Web Workers Polyfill的特定衍生产品:

function gen_multipart(title, description, image, mimetype) {
    var multipart = [ "..." ].join(''); // See question for the source
    var uint8array = new Uint8Array(multipart.length);
    for (var i=0; i<multipart.length; i++) {
        uint8array[i] = multipart.charCodeAt(i) & 0xff;
    }
    return uint8array.buffer; // <-- This is an ArrayBuffer object!
}
Run Code Online (Sandbox Code Playgroud)

使用时脚本变得更有效,.readAsArrayBuffer而不是.readAsBinaryString:

function gen_multipart(title, description, image, mimetype) {
    image = new Uint8Array(image); // Wrap in view to get data

    var before = ['Media ... ', 'Content-Type:', mimetype, "\n\n"].join('');
    var after = '\n--END_OF_PART--';
    var size = before.length + image.byteLength + after.length;
    var uint8array = new Uint8Array(size);
    var i = 0;

    // Append the string.
    for (; i<before.length; i++) {
        uint8array[i] = before.charCodeAt(i) & 0xff;
    }

    // Append the binary data.
    for (var j=0; j<image.byteLength; i++, j++) {
        uint8array[i] = image[j];
    }

    // Append the remaining string
    for (var j=0; j<after.length; i++, j++) {
        uint8array[i] = after.charCodeAt(j) & 0xff;
    }
    return uint8array.buffer; // <-- This is an ArrayBuffer object!
}
Run Code Online (Sandbox Code Playgroud)