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

后端 未结 2 746
失恋的感觉
失恋的感觉 2020-12-04 20:14

I have this content script that downloads some binary data using XHR, which is sent later to the background script:

var self = this;
var xhr = new XMLHttpReq         


        
2条回答
  •  攒了一身酷
    2020-12-04 21:04

    Messages passed between a Content Script and a background page are JSON-serialized.

    If you want to transfer an ArrayBuffer object through a JSON-serialized channel, wrap the buffer in a view, before and after transferring.

    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 ArrayBuffers and typed arrays, but the method can also be applied to File and Blob objects, by using the FileReader API.

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

    • new Uint8Array(xhr.response) is used to create a view of the ArrayBuffer, so that the individual bytes can be read.
    • Array.apply(null, ) is used to create a plain array, using the keys from the Uint8Array view. 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. 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.

    Implementation in your Google Chrome extension:

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

    • MDN: Typed Arrays
    • MDN: ArrayBuffer
    • MDN: Uint8Array
    • MDN: .apply
    • Google Chrome Extension docs: Messaging > Simple one-time requests
      "This lets you send a one-time JSON-serializable message from a content script to extension, or vice versa, respectively"
    • SO bonus: Upload a File in a Google Chrome Extension - Using a Web worker to request, validate, process and submit binary data.

提交回复
热议问题