XMLHttpRequest:多部分/相关POST,XML和图像作为有效载荷 [英] XMLHttpRequest: Multipart/Related POST with XML and image as payload

查看:176
本文介绍了XMLHttpRequest:多部分/相关POST,XML和图像作为有效载荷的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试从Chrome扩展程序中将图片(包含元数据)发布到Picasa Webalbums。请注意,正如我在此处所述,使用Content-Type image / xyz的常规帖子可以正常工作。不过,我希望包含说明/关键字和协议规范描述了>多部分/相关格式与XML和数据部分。



我通过HTML5 FileReader和用户文件输入获取数据。我使用

  FileReader.readAsBinaryString(file);来检索二元
字符串。

假设这是我的回调代码,一旦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);
//添加OAuth令牌
xhr.setRequestHeader(Authorization,oauth.getAuthorizationHeader(url,method,''));
xhr.onreadystatechange = function(data){
if(xhr.readyState == 4){
// .. handle response
}
};
xhr.send(request);

$ / code>

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

 函数gen_multipart(title,description,image,mimetype){
var multipart = ['Media multipart posting',\\\
,'--END_OF_PART',\\\

'Content -type:application / atom + xml',\\\
,\\\

< entry xmlns ='http://www.w3.org/2005/Atom'> ,< title>',title,'< / title>',
'< summary>',description,'< / summary>',
'< category scheme = http://schemas.google.com/g/2005#kindterm =http://schemas.google.com/photos/2007#photo/ >',
'< / entry>',\ n,'--END_OF_PART',\\\

'Content-Type:',mimetype,\ n \ n,
图片,\ n,--END_OF_PART--];
return multipart.join();
}

问题是,POST负载与原始图像数据不同,从而导致一个错误的请求(Picasa不会接受图像),虽然它在使用

  xhr.send(文件)//将内容类型设置为file.type 

我的问题是,如何获得 real 二进制图像将其包含在multipart中?我认为它是通过将它附加到xml字符串而被破坏的,但我似乎无法解决它。



请注意,由于 Picasa中的旧bug ,base64不是解决方案。

解决方案

XMLHttpRequest规范指出,使用 .send()方法发送的数据转换为unicode,并且编码为UTF-8。 p>

推荐的上传二进制数据的方式是通过 FormData API。但是,由于您不只是上传文件,而是将二进制数据封装在XML中,所以此选项无用。



解决方案可以在源代码中找到的 Web Workers Polyfill FormData ,其中I当我遇到类似的问题时我写了。为了防止Unicode转换,所有数据都被添加到一个数组中,最后作为 ArrayBuffer 。传输时不会触及字节序列, per specification



下面的代码是一个特定的衍生物,基于 FormData for Web Workers Polyfill

  function gen_multipart(title,description, image,mimetype){
var multipart = [...] .join(''); //查看问题的源
var uint8array = new Uint8Array(multipart.length);
for(var i = 0; i< multipart.length; i ++){
uint8array [i] = multipart.charCodeAt(i)& 0xFF的;
}
return uint8array.buffer; //< - 这是一个ArrayBuffer对象!
}

使用 .readAsArrayBuffer 而不是 .readAsBinaryString
$ b $ pre $ function gen_multipart(title,description,image,mimetype){
image = new Uint8Array(image); //换行以获取数据

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

//追加字符串。 $; $ b $ for((i< Oncelength; i ++)){
uint8array [i] = betwcharCodeAt(i)& 0xFF的;
}

//追加二进制数据。
for(var j = 0; j< image.byteLength; i ++,j ++){
uint8array [i] = image [j];
}

//追加剩余的字符串
for(var j = 0; j< after.length; i ++,j ++){
uint8array [i] = after.charCodeAt(j)& 0xFF的;
}
return uint8array.buffer; //< - 这是一个ArrayBuffer对象!
}


I'm trying to POST an image (with Metadata) to Picasa Webalbums from within a Chrome-Extension. Note that a regular post with Content-Type image/xyz works, as I described here. However, I wish to include a description/keywords and the protocol specification describes a multipart/related format with a XML and data part.

I'm getting the Data through HTML5 FileReader and user file input. I retrieve a binary String using

FileReader.readAsBinaryString(file);

Assume this is my callback code once the FileReader has loaded the string:

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);
}   

The gen_multipart function just generates the multipart from the input values and the XML template and produces the exact same output as the specification (apart from ..binary image data..), but for sake of completeness, here it is:

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("");
}

The problem is, that the POST payload differs from the raw image data, and thus leads to a Bad Request (Picasa won't accept the image), although it worked fine when using

xhr.send(file) // With content-type set to file.type

My question is, how do I get the real binary image to include it in the multipart? I assume it is mangled by just appending it to the xml string, but I can't seem to get it fixed.

Note that due to an old bug in Picasa, base64 is not the solution.

解决方案

The XMLHttpRequest specification states that the data send using the .send() method is converted to unicode, and encoded as UTF-8.

The recommended way to upload binary data is through FormData API. However, since you're not just uploading a file, but wrapping the binary data within XML, this option is not useful.

The solution can be found in the source code of the FormData for Web Workers Polyfill, which I've written when I encountered a similar problem. To prevent the Unicode-conversion, all data is added to an array, and finally transmitted as an ArrayBuffer. The byte sequences are not touched on transmission, per specification.

The code below is a specific derivative, based on the FormData 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!
}

The script becomes more efficient when you use .readAsArrayBuffer instead of .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!
}

这篇关于XMLHttpRequest:多部分/相关POST,XML和图像作为有效载荷的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆