我有这个内容脚本,它使用XHR下载一些二进制数据,后来发送到后台脚本:
var self = this;
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
if (this.status == 200) {
self.data = {
data: xhr.response,
contentType: xhr.getResponseHeader('Content-Type')
};
}
};
xhr.send();
... later ...
sendResponse({data: self.data});
在后台脚本中收到这些数据之后,我想形成另一个XHR请求,将这个二进制数据上传到我的服务器,所以我这样做:
var formData = new FormData();
var bb = new WebKitBlobBuilder();
bb.append(data.data);
formData.append("data", bb.getBlob(data.contentType));
var req = new XMLHttpRequest();
req.open("POST", serverUrl);
req.send(formData);
问题是上传到服务器的文件只包含这个字符串:“[object Object]”。我想这是因为ArrayBuffer类型在从内容进程转移到后台时以某种方式丢失了?我该如何解决?
答案 0 :(得分:18)
内容脚本和后台页面之间传递的消息是JSON序列化的。
如果要通过JSON序列化通道传输ArrayBuffer
对象,请在传输之前和之后将缓冲区包装在视图中。
我展示了一个孤立的示例,因此解决方案通常适用,而不仅仅是在您的情况下。该示例显示了如何传递ArrayBuffer
和类型化数组,但该方法也可以通过FileReader
API应用于File
和Blob
个对象。
// In your case: self.data = { data: new Uint8Array(xhr.response), ...
// Generic example:
var example = new ArrayBuffer(10);
var data = {
// Create a view
data: Array.apply(null, new Uint8Array(example)),
contentType: 'x-an-example'
};
// Transport over a JSON-serialized channel. In your case: sendResponse
var transportData = JSON.stringify(data);
//"{"data":[0,0,0,0,0,0,0,0,0,0],"contentType":"x-an-example"}"
// At the receivers end. In your case: chrome.extension.onRequest
var receivedData = JSON.parse(transportData);
// data.data is an Object, NOT an ArrayBuffer or Uint8Array
receivedData.data = new Uint8Array(receivedData.data).buffer;
// Now, receivedData is the expected ArrayBuffer object
此解决方案已在Chrome 18和Firefox中成功测试过。
new Uint8Array(xhr.response)
用于创建ArrayBuffer
的视图,以便可以读取各个字节。 Array.apply(null, <Uint8Array>)
用于使用Uint8Array
视图中的键创建普通数组。此步骤减小了序列化消息的大小。 警告:此方法仅适用于少量数据。当类型化数组的大小超过125836时,将抛出RangeError。如果需要处理大量数据,请使用其他方法在类型化数组和普通数组之间进行转换。
在接收方端,可以通过creating a new Uint8Array
获取原始缓冲区,并阅读buffer
attribute。
// Part of the Content script
self.data = {
data: Array.apply(null, new Uint8Array(xhr.response)),
contentType: xhr.getResponseHeader('Content-Type')
};
...
sendResponse({data: self.data});
// Part of the background page
chrome.runtime.onMessage.addListener(function(data, sender, callback) {
...
data.data = new Uint8Array(data.data).buffer;
ArrayBuffer
Uint8Array
<Function> .apply
答案 1 :(得分:8)
有一种更好的方法可以在 相同的 Chrome扩展程序的任何部分(内容脚本,背景页面和普通页面)之间传递Blob
(或ArrayBuffer
) )然后创建一个普通的JS Array 或一个二进制字符串并在消息体中传递这个(有时非常大的)数据块!请记住,它们在发送方端是JSONified,然后在接收方端进行unJSONified!
只需创建并传递Object URL
:
sendResponse(URL.createObjectURL(blob));
或首先从ArrayBuffer创建Blob:
var blob = new Blob([ arrayBuffer ], { type: 'image/jpeg' });
sendResponse(URL.createObjectURL(blob));
BTW XMLHttpRequest 2
可以同时返回Blob
和ArrayBuffer
。
URL.revokeObjectURL(objectURL)