ArrayBuffer
とビューはECMA標準、JavaScriptの一部です。
ブラウザには、File APIで説明されている、特にBlob
など、追加のより高レベルのオブジェクトがあります。
Blob
は、オプションの文字列type
(通常はMIMEタイプ)、およびblobParts
(他のBlob
オブジェクト、文字列、BufferSource
のシーケンス)で構成されます。
コンストラクタの構文は次のとおりです。
new Blob(blobParts, options);
blobParts
は、Blob
/BufferSource
/String
値の配列です。options
オプションオブジェクトtype
–Blob
の型、通常はMIMEタイプ(例:image/png
)、endings
–Blob
を現在のOSの改行コード(\r\n
または\n
)に合わせるために、改行コードを変換するかどうか。デフォルトは"transparent"
(何もせず)、"native"
(変換する)も可能です。
例として
// create Blob from a string
let blob = new Blob(["<html>…</html>"], {type: 'text/html'});
// please note: the first argument must be an array [...]
// create Blob from a typed array and strings
let hello = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" in binary form
let blob = new Blob([hello, ' ', 'world'], {type: 'text/plain'});
Blob
のスライスは、次のように抽出できます。
blob.slice([byteStart], [byteEnd], [contentType]);
byteStart
– 開始バイト、デフォルトは0。byteEnd
– 最後のバイト(排他的、デフォルトは最後まで)。contentType
– 新しいblobのtype
、デフォルトはソースと同じ。
引数はarray.slice
に似ており、負の数も許容されます。
Blob
オブジェクトは不変です。Blob
内のデータを直接変更することはできませんが、Blob
の一部をスライスしたり、それらから新しいBlob
オブジェクトを作成したり、それらを新しいBlob
に混ぜ合わせたりすることができます。
この動作はJavaScriptの文字列と似ています。文字列内の文字を変更することはできませんが、新しい修正された文字列を作成することはできます。
BlobをURLとして
Blobは、その内容を表示するために、<a>
、<img>
、その他のタグのURLとして簡単に使用できます。
type
のおかげで、Blob
オブジェクトのダウンロード/アップロードも可能になり、type
はネットワークリクエストで自然にContent-Type
になります。
簡単な例から始めましょう。リンクをクリックすると、「hello world」の内容を持つ動的に生成されたBlob
をファイルとしてダウンロードします。
<!-- download attribute forces the browser to download instead of navigating -->
<a download="hello.txt" href='#' id="link">Download</a>
<script>
let blob = new Blob(["Hello, world!"], {type: 'text/plain'});
link.href = URL.createObjectURL(blob);
</script>
JavaScriptで動的にリンクを作成し、link.click()
でクリックをシミュレートすることもでき、ダウンロードが自動的に開始されます。
HTMLを使用せずに、動的に作成されたBlob
をユーザーにダウンロードさせる同様のコードを次に示します。
let link = document.createElement('a');
link.download = 'hello.txt';
let blob = new Blob(['Hello, world!'], {type: 'text/plain'});
link.href = URL.createObjectURL(blob);
link.click();
URL.revokeObjectURL(link.href);
URL.createObjectURL
はBlob
を受け取り、blob:<origin>/<uuid>
という形式の一意のURLを作成します。
link.href
の値は次のようになります。
blob:https://javascriptinfo.dokyumento.jp/1e67e00e-860d-40a5-89ae-6ab0cbee6273
URL.createObjectURL
によって生成された各URLについて、ブラウザはURL→Blob
のマッピングを内部的に保存します。そのため、このようなURLは短くなりますが、Blob
にアクセスできます。
生成されたURL(およびそれに対応するリンク)は、開いている現在のドキュメント内でのみ有効です。そして、それは<img>
、<a>
、基本的にURLを期待する他のオブジェクトでBlob
を参照することを可能にします。
ただし、副作用があります。Blob
のマッピングがある間、Blob
自体はメモリ内に存在します。ブラウザはそれを解放できません。
ドキュメントのアンロード時にマッピングは自動的にクリアされるため、Blob
オブジェクトはその時点で解放されます。しかし、アプリケーションが長寿命である場合、それはすぐに起こりません。
したがって、URLを作成すると、Blob
は、もはや必要でなくても、メモリ内に残ります。
URL.revokeObjectURL(url)
は、内部マッピングからの参照を削除するため、Blob
を削除し(他の参照がない場合)、メモリを解放できます。
最後の例では、Blob
を一度だけ、即時ダウンロードのために使用することを意図しているため、URL.revokeObjectURL(link.href)
をすぐに呼び出します。
クリック可能なHTMLリンクを含む前の例では、URL.revokeObjectURL(link.href)
を呼び出しません。これは、Blob
のURLが無効になるためです。取り消し後、マッピングが削除されると、URLは機能しなくなります。
Blobをbase64に変換
URL.createObjectURL
の代替手段として、Blob
をbase64エンコードされた文字列に変換する方法があります。
このエンコーディングは、バイナリデータを、0〜64のASCIIコードを持つ、非常に安全な「読み取り可能な」文字の文字列として表します。そして、さらに重要なのは、このエンコーディングを「data-url」で使用できることです。
data urlはdata:[<mediatype>][;base64],<data>
という形式です。「通常の」URLと同様に、どこでも使用できます。
例えば、スマイリーフェイスを次に示します。
<img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7">
ブラウザは文字列をデコードして画像を表示します:
Blob
をbase64に変換するには、組み込みのFileReader
オブジェクトを使用します。これは、複数のフォーマットでBlobからデータを読み取ることができます。次の章では、より詳細に説明します。
base-64経由でのblobのダウンロードデモを次に示します。
let link = document.createElement('a');
link.download = 'hello.txt';
let blob = new Blob(['Hello, world!'], {type: 'text/plain'});
let reader = new FileReader();
reader.readAsDataURL(blob); // converts the blob to base64 and calls onload
reader.onload = function() {
link.href = reader.result; // data url
link.click();
};
Blob
のURLを作成する両方の方法は使用できますが、通常はURL.createObjectURL(blob)
の方がシンプルで高速です。
- メモリを気にしている場合は、それらを呼び消す必要があります。
- Blobへの直接アクセス、「エンコード/デコード」なし
- 何も呼び消す必要はありません。
- 大きな
Blob
オブジェクトのエンコーディングにおけるパフォーマンスとメモリ損失。
画像をBlobに変換
画像、画像の一部、またはページのスクリーンショットのBlob
を作成できます。これは、どこかにアップロードする場合に便利です。
画像操作は<canvas>
要素を使用して行われます。
- canvas.drawImageを使用して、キャンバスに画像(またはその一部)を描画します。
Blob
を作成し、完了したらそれを使用してcallback
を実行するキャンバスメソッド.toBlob(callback, format, quality)を呼び出します。
以下の例では、画像は単にコピーされていますが、blobを作成する前に、キャンバス上で画像を切り取ったり変換したりすることもできます。
// take any image
let img = document.querySelector('img');
// make <canvas> of the same size
let canvas = document.createElement('canvas');
canvas.width = img.clientWidth;
canvas.height = img.clientHeight;
let context = canvas.getContext('2d');
// copy image to it (this method allows to cut image)
context.drawImage(img, 0, 0);
// we can context.rotate(), and do many other things on canvas
// toBlob is async operation, callback is called when done
canvas.toBlob(function(blob) {
// blob ready, download it
let link = document.createElement('a');
link.download = 'example.png';
link.href = URL.createObjectURL(blob);
link.click();
// delete the internal blob reference, to let the browser clear memory from it
URL.revokeObjectURL(link.href);
}, 'image/png');
コールバックの代わりにasync/await
を使用する場合は
let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));
https://github.com/niklasvh/html2canvasなどのライブラリを使用すると、ページのスクリーンショットを撮ることができます。これは、ページを巡回し、<canvas>
に描画するだけです。その後、上記と同じ方法でBlob
を取得できます。
BlobからArrayBufferへ
Blob
コンストラクタを使用すると、あらゆるBufferSource
を含む、ほぼあらゆるものからblobを作成できます。
しかし、低レベルの処理を行う必要がある場合は、blob.arrayBuffer()
から最下位のArrayBuffer
を取得できます。
// get arrayBuffer from blob
const bufferPromise = await blob.arrayBuffer();
// or
blob.arrayBuffer().then(buffer => /* process the ArrayBuffer */);
Blobからストリームへ
2GBを超えるblobの読み書きを行う場合、arrayBuffer
の使用はメモリ集約的になります。この時点で、blobをストリームに直接変換できます。
ストリームは、部分的に読み取り(または書き込み)できる特殊なオブジェクトです。ここでは範囲外ですが、例を以下に示し、https://developer.mozilla.org/en-US/docs/Web/API/Streams_APIで詳細を読むことができます。ストリームは、部分的に処理するのに適したデータに便利です。
Blob
インターフェースのstream()
メソッドは、読み取り時にBlob
に含まれるデータを返すReadableStream
を返します。
その後、次のようにして読み取ることができます。
// get readableStream from blob
const readableStream = blob.stream();
const stream = readableStream.getReader();
while (true) {
// for each iteration: value is the next blob fragment
let { done, value } = await stream.read();
if (done) {
// no more data in the stream
console.log('all blob processed.');
break;
}
// do something with the data portion we've just read from the blob
console.log(value);
}
まとめ
ArrayBuffer
、Uint8Array
、その他のBufferSource
は「バイナリデータ」ですが、Blobは「型付きバイナリデータ」を表します。
これにより、ブラウザで非常に一般的なアップロード/ダウンロード操作にBlobが便利です。
XMLHttpRequest、fetchなど、Webリクエストを実行するメソッドは、他のバイナリ型と同様に、Blob
をネイティブに使用できます。
Blob
と低レベルのバイナリデータ型の間を簡単に変換できます。
new Blob(...)
コンストラクタを使用して、型付き配列からBlob
を作成できます。blob.arrayBuffer()
を使用してBlobからArrayBuffer
を取得し、低レベルのバイナリ処理のためにその上にビューを作成できます。
変換ストリームは、大きなBlobを扱う必要がある場合に非常に便利です。BlobからReadableStream
を簡単に作成できます。Blob
インターフェースのstream()
メソッドはReadableStream
を返し、読み取り時にBlobに含まれるデータが返されます。
コメント
<code>
タグを使用し、複数行の場合は<pre>
タグで囲み、10行を超える場合は、サンドボックス(plnkr、jsbin、codepen…)を使用してください。