简体   繁体   English

Chrome扩展程序:如何将ArrayBuffer或Blob从内容脚本传递到后台而不会丢失其类型?

[英]Chrome extension: how to pass ArrayBuffer or Blob from content script to the background without losing its type?

I have this content script that downloads some binary data using XHR, which is sent later to the background script: 我有这个内容脚本,使用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});

After receiving this data in background script, I'd like to form another XHR request that uploads this binary data to my server, so I do: 在后台脚本中接收到这些数据之后,我想形成另一个将这个二进制数据上传到我的服务器的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);

The problem is that the file uploaded to the server contains just this string: "[object Object]". 问题是上传到服务器的文件只包含这个字符串:“[object Object]”。 I guess this happens because ArrayBuffer type is lost somehow while transferring it from content process to the background? 我想这是因为ArrayBuffer类型在从内容进程转移到后台时以某种方式丢失了? How can I solve that? 我怎么解决这个问题?

Messages passed between a Content Script and a background page are JSON-serialized. 在内容脚本和后台页面之间传递的消息是JSON序列化的。

If you want to transfer an ArrayBuffer object through a JSON-serialized channel, wrap the buffer in a view, before and after transferring. 如果要通过JSON序列化通道传输ArrayBuffer对象,请在传输之前和之后将缓冲区包装在视图中。

I show an isolated example, so that the solution is generally applicable, and not just in your case. 我展示了一个孤立的示例,以便解决方案通常适用,而不仅仅是在您的情况下。 The example shows how to pass around ArrayBuffer s and typed arrays, but the method can also be applied to File and Blob objects, by using the FileReader API. 该示例显示了如何传递ArrayBuffer和类型化数组,但该方法也可以通过使用FileReader API应用于FileBlob对象。

// 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

This solution has been tested successfully in Chrome 18 and Firefox. 此解决方案已在Chrome 18和Firefox中成功测试过。

  • new Uint8Array(xhr.response) is used to create a view of the ArrayBuffer , so that the individual bytes can be read. new Uint8Array(xhr.response)用于创建ArrayBuffer的视图,以便可以读取各个字节。
  • Array.apply(null, <Uint8Array>) is used to create a plain array, using the keys from the Uint8Array view. Array.apply(null, <Uint8Array>)用于使用Uint8Array视图中的键创建普通数组。 This step reduces the size of the serialized message. 此步骤减小了序列化消息的大小。 WARNING: This method only works for small amounts of data. 警告:此方法仅适用于少量数据。 When the size of the typed array exceeds 125836, a RangeError will be thrown. 当类型化数组的大小超过125836时,将抛出RangeError。 If you need to handle large pieces of data, use other methods to do the conversion between typed arrays and plain arrays. 如果需要处理大量数据,请使用其他方法在类型化数组和普通数组之间进行转换。

  • At the receivers end, the original buffer can be obtained by creating a new Uint8Array , and reading the buffer attribute . 在接收器端,可以通过创建新的Uint8Array并读取buffer属性来获取原始缓冲区。

Implementation in your Google Chrome extension: 在您的Google Chrome扩展程序中实施:

// 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;

Documentation 文档

There is a better way to pass Blob (or ArrayBuffer ) between any parts of the same Chrome extension (content scripts, background page and ordinary pages) then creating an ordinary JS Array or a binary string and passing this (sometimes extremely big) chunk of data in a message body! 有一种更好的方法可以在同一个 Chrome扩展程序的任何部分(内容脚本,背景页面和普通页面 )之间传递Blob (或ArrayBuffer ),然后创建一个普通的JS 数组二进制字符串并传递这个(有时非常大)的块消息体中的数据! Remember that they are JSONified at the sender's end and then unJSONified at the receiver's end! 请记住,它们在发送方端是JSONified,然后在接收方端进行unJSONified!

Just create and pass Object URL : 只需创建并传递Object URL

sendResponse(URL.createObjectURL(blob));

or create Blob first from ArrayBuffer: 或者首先从ArrayBuffer创建Blob:

var blob = new Blob([ arrayBuffer ], { type: 'image/jpeg' });
sendResponse(URL.createObjectURL(blob));

BTW XMLHttpRequest 2 can return both Blob and ArrayBuffer . BTW XMLHttpRequest 2可以返回BlobArrayBuffer

Notes 笔记

  • Object URLs can be alive for a qute long time so if you don't need data anymore don't forget to release such URLs calling URL.revokeObjectURL(objectURL) 对象URL可以长时间处于活动状态,因此如果您不再需要数据,请不要忘记发布调用URL.revokeObjectURL(objectURL) URL
  • Object URLs as any URL are subject of Cross-Origin restrictions but all parts of your extension are in the same origin of course. 对象的网址为任何URL如有的跨源限制,但分机的所有部分都在,当然相同的起源。
  • BTW: I got a 4x performance boost when start passing such URLs instead of passing data itself in my Chrome extension! 顺便说一句:开始传递这些URL时,我的性能提升4倍 ,而不是在Chrome扩展程序中传递数据本身! (My data were quite big images.) (我的数据非常大。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在Chrome扩展程序中将参数从后台脚本传递到内容脚本? - How to pass parameter from a background script to content script in chrome extension? 将消息传递给Chrome扩展程序内容脚本,而不将其指定为后台脚本 - Pass message to Chrome extension content script without specifying it as a background script Chrome扩展程序 - 从背景到上下文脚本的Blob - Chrome Extension - Blob from Background to Context script 如何通过上下文菜单将消息从Chrome扩展程序后台脚本传递到JavaScript中的内容脚本? - How to pass a message from a Chrome extension background script to a content script in JavaScript via the context menu? 如何将 chrome 扩展中的选定文本从后台脚本传递到内容脚本? - how do i pass selected text in chrome extension from background script to content script? 如何将数组从 background.js 传递给 inject.js(内容)脚本(Chrome 扩展程序) - How to pass an array from background.js to inject.js (content) script (Chrome Extension) chrome扩展程序,将数据从内容传递到背景 - chrome extension, pass data from content to background Chrome扩展程序-如何将DOM从内容脚本发送到后台页面? - Chrome Extension - How to message DOM from content script to background page? 如何将Chrome扩展程序中的blob传递到Chrome应用 - How to pass a blob from a Chrome extension to a Chrome app 从内容脚本发送消息到后台脚本会破坏chrome扩展 - Sending message from content script to background script breaks chrome extension
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM