14

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

問題は、サーバーにアップロードされたファイルに「[objectObject]」という文字列だけが含まれていることです。これは、コンテンツプロセスからバックグラウンドに転送するときにArrayBufferタイプが何らかの理由で失われたために発生すると思いますか?どうすればそれを解決できますか?

4

2 に答える 2

21

コンテンツスクリプトとバックグラウンドページの間で受け渡されるメッセージはJSONでシリアル化されます。

JSONでシリアル化されたチャネルを介してオブジェクトを転送する場合は、ArrayBuffer転送の前後にバッファーをビューでラップします。

孤立した例を示します。これにより、ソリューションは、あなたの場合だけでなく、一般的に適用可能になります。この例は、sと型付き配列を渡す方法を示していますが、 APIを使用して、このメソッドをオブジェクトにArrayBuffer適用することもできます。FileBlobFileReader

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

このソリューションは、Chrome18とFirefoxで正常にテストされています。

  • new Uint8Array(xhr.response)のビューを作成するために使用されるためArrayBuffer、個々のバイトを読み取ることができます。
  • Array.apply(null, <Uint8Array>)ビューのキーを使用して、プレーン配列を作成するために使用されますUint8Array。この手順により、シリアル化されたメッセージのサイズが縮小されます。警告:この方法は、少量のデータに対してのみ機能します。型指定された配列のサイズが125836を超えると、RangeErrorがスローされます。大量のデータを処理する必要がある場合は、他の方法を使用して、型付き配列とプレーン配列の間の変換を実行します。

  • 受信側では、新しいバッファを作成し、Uint8Arraybuffer属性を読み取ることで、元のバッファを取得できます。

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;

ドキュメンテーション

于 2012-04-09T11:37:15.180 に答える
16

同じChrome拡張機能の任意の部分(コンテンツスクリプト、バックグラウンドページ、通常のページ)間で受け渡しBlob(または) 、通常のJS配列またはバイナリ文字列を作成し、この(場合によっては非常に大きな)データのチャンクをメッセージ本文!それらは送信者側でJSON化され、受信者側で非JSON化されることを忘れないでください。ArrayBuffer

作成して渡すだけObject URLです:

sendResponse(URL.createObjectURL(blob));

または、最初にArrayBufferからBlobを作成します。

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

ところで、とXMLHttpRequest 2の両方Blobを返すことができArrayBufferます。

ノート

  • オブジェクトのURLは非常に長い間存続する可能性があるため、データが不要になった場合は、そのようなURLを呼び出すことを忘れないでください。URL.revokeObjectURL(objectURL)
  • 任意のURLとしてのオブジェクトURLはクロスオリジン制限の対象ですが、拡張機能のすべての部分はもちろん同じオリジンにあります。
  • ところで:Chrome拡張機能でデータ自体を渡す代わりに、そのようなURLを渡し始めると、パフォーマンスが4倍向上しました。(私のデータはかなり大きな画像でした。)
于 2013-09-15T16:18:52.890 に答える