XMLHttpRequest:以 XML 和图像作为有效负载的多部分/相关 POST
我正在尝试从 Chrome 扩展程序中将图像(带有元数据)发布到 Picasa 网络相册.请注意,正如我在此处所述,使用 Content-Type image/xyz 的常规帖子有效.但是,我希望包含描述/关键字和 协议规范 描述了 multipart/related 格式 带有 XML 和数据部分.
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.
我通过 HTML5 FileReader 和用户文件输入获取数据.我检索一个二进制文件使用
I'm getting the Data through HTML5 FileReader and user file input. I retrieve a binary String using
FileReader.readAsBinaryString(file);
假设这是我在 FileReader 加载字符串后的回调代码:
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);
}
gen_multipart 函数只是从输入值和 XML 模板生成多部分,并产生完全相同的输出 作为规范(除了..二进制图像数据..),但为了完整起见,这里是:
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', "
", '--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#kind" term="http://schemas.google.com/photos/2007#photo" />',
'</entry>', "
", '--END_OF_PART', "
",
'Content-Type:', mimetype, "
",
image, "
", '--END_OF_PART--'];
return multipart.join("");
}
问题是,POST 有效负载与原始图像数据不同,因此会导致错误请求(Picasa 不会接受图像),尽管在使用时它工作正常
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
我的问题是,如何让 real 二进制图像包含在多部分中?我认为它只是通过将它附加到 xml 字符串而被破坏,但我似乎无法修复它.
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.
请注意,由于 Picasa 中存在 旧错误,base64 不是解决方案.
Note that due to an old bug in Picasa, base64 is not the solution.
推荐答案
XMLHttpRequest 规范 声明使用 .send()
方法发送的数据将转换为 unicode,并编码为 UTF-8.
The XMLHttpRequest specification states that the data send using the .send()
method is converted to unicode, and encoded as UTF-8.
推荐的二进制数据上传方式是通过FormData
API.但是,由于您不只是上传文件,而是将二进制数据包装在 XML 中,因此此选项没有用.
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 for Web Workers Polyfill,这是我遇到类似问题时写的.为了防止 Unicode 转换,所有数据都添加到一个数组中,最后作为 传输数组缓冲区
.根据规范,字节序列在传输时不会被触及.
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.
以下代码是基于 的特定衍生代码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!
}
当您使用 .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, "
"].join('');
var after = '
--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!
}
相关文章