javascript XMLHttpRequest:以 XML 和图像作为负载的多部分/相关 POST
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8262266/
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
XMLHttpRequest: Multipart/Related POST with XML and image as payload
提问by oliverguenther
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 specificationdescribes a multipart/related formatwith a XML and data part.
我正在尝试从 Chrome 扩展程序中将图像(带有元数据)发布到 Picasa Webalbums。请注意,如我在此处所述,带有 Content-Type image/xyz 的常规帖子有效。但是,我希望包含一个描述/关键字,并且协议规范描述了一个带有 XML 和数据部分的多部分/相关格式。
I'm getting the Data through HTML5 FileReader and user file input. I retrieve a binary String using
我通过 HTML5 FileReader 和用户文件输入获取数据。我使用检索二进制字符串
FileReader.readAsBinaryString(file);
Assume this is my callback code once the FileReader has loaded the string:
假设这是 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);
}
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:
gen_multipart 函数只是从输入值和 XML 模板生成 multipart,并产生与规范完全相同的输出(除了 ..binary image data..),但为了完整起见,这里是:
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
问题是,POST 负载与原始图像数据不同,因此导致错误请求(Picasa 不会接受图像),尽管它在使用时工作正常
xhr.send(file) // With content-type set to file.type
My question is, how do I get the realbinary 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.
我的问题是,如何获得真正的二进制图像以将其包含在多部分中?我认为它只是通过将它附加到 xml 字符串而被破坏,但我似乎无法修复它。
Note that due to an old bug in Picasa, base64 is not the solution.
回答by Rob W
The XMLHttpRequest specificationstates that the data send using the .send()
method is converted to unicode, and encoded as UTF-8.
所述的XMLHttpRequest说明书指出该数据发送使用所述.send()
方法转换为Unicode,并且编码为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.
上传二进制数据的推荐方式是通过FormData
API。但是,由于您不仅要上传文件,还要将二进制数据包装在 XML 中,因此此选项没有用。
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.
解决方法可以在FormData for Web Workers Polyfill的源代码中找到,我遇到类似问题时写的。为了防止 Unicode 转换,所有数据都被添加到一个数组中,最后以ArrayBuffer
. 根据规范,字节序列不涉及传输。
The code below is a specific derivative, based on the FormData for Web Workers Polyfill:
下面的代码是一个特定的衍生物,基于Web Workers Polyfill的FormData:
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
:
当您使用.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!
}