このエントリで紹介するBlob
やFile
、FileReader
はHTML5で利用可能になったAPIで、ECMAScriptで定義されているわけではない。
そのため、Node.jsには存在せず、ブラウザ環境でのみ利用できる。
Blob
Blob
は、バイナリデータを表すimmutableなオブジェクト。
const blob = new Blob(['<xml>foo</xml>'], {type: 'text/xml'}); console.log(blob); // Blob(14) {size: 14, type: "text/xml"}
第二引数で設定しているtype
で、MIMEを設定できる。
何も設定しなかった場合は空の文字列になる。
File
File
はその名の通りファイルを表すオブジェクトで、Blob
を継承している。
const file = new File(['<xml>foo</xml>'], 'example.xml', {type: 'text/xml'}); console.log(file); // name: "example.xml", type: "text/xml", size: 14, などのプロパティを持つ console.log(file instanceof File); // true console.log(file instanceof Blob); // true
new File()
の第二引数で、ファイル名を指定する(必須)。それ以外のプロパティは第三引数のオブジェクトで指定するが、これはオプションであり必須ではない。
<input type="file">
やDnDイベントで、ローカルファイルをFile
として取得できる。
// ドロップで取得するケース // e はdropイベントのイベントオブジェクト e.dataTransfer.files
// <input type="file"> で取得するケース // e はchangeイベントのイベントオブジェクト e.target.files
files
はその名の通り、File
の配列。
2018/1/25 追記
はてなブックマークで以下のコメントを頂きました。
id:daichirata filesはFileの配列ではなくFileList、配列として扱いたいならArray.fromとかしないと駄目なのでは
はい、仰る通りです。
配列のように添え字を使って各要素にアクセスできる、というだけで、配列ではありません。
{0: File, 1: File, ...}
という形式のオブジェクトですね。
大変失礼しました。
const {files} = e.dataTransfer; console.log(files); // FileList {0: File(6740), 1: File(14292), length: 2} console.log(files[0]); // File(6740) console.log(files[0] instanceof File); // true console.log(files instanceof FileList); // true console.log(Array.isArray(files)); // false const array = Array.from(files); console.log(array); // [File(6740), File(14292)] console.log(Array.isArray(array)); // true console.log(files[0] === array[0]); // true
追記終わり
FileReader
Blob
オブジェクトの中身に直接アクセスすることは出来ない。当然、それを継承しているFile
オブジェクトも同様である。
アクセスしたい場合はFileReader
を使う。
まず、FileReader
のインスタンスを作成する。
次に、onload
を設定する。これは、読み込みが終わったときに呼び出されるコールバック関数。
そして、readAsXXX
のメソッドを使ってBlob
を読み込む。
const blob = new Blob(['<xml>foo</xml>'], {type: 'text/xml'}); console.log(blob); // Blob(14) {size: 14, type: "text/xml"} const reader = new FileReader(); reader.onload = () => { console.log(reader.result); }; reader.readAsText(blob); // <xml>foo</xml> reader.readAsArrayBuffer(blob); // ArrayBuffer(14) {} reader.readAsDataURL(blob); // data:text/xml;base64,PHhtbD5mb288L3htbD4= reader.readAsBinaryString(blob); // <xml>foo</xml>
最後に使っているreadAsBinaryString()
は、現在では非推奨になっている。
バイナリファイルをURLで表現する
DataURL
上記のサンプルでも使っているreadAsDataURL()
は、Blob
をDataURL形式で読み出すメソッド。
では、DataURLとは何か。
これは、データをURL(data:
で始まる文字列)で表現するための仕組み。
バイナリファイルの場合はBase64という形式でエンコードする。
サンプルで出力されたdata:text/xml;base64,PHhtbD5mb288L3htbD4=
をブラウザのアドレスバーに入れると、<xml>foo</xml>
と表示されることを確認できる。
URLの代わりとして使えるため、例えば、img要素のsrc属性にDataURLを使うことも出来る。
データそのものをURLで表現しているため、Cookieなどに保存したり、サーバーに渡したりすることが出来る。
BlobURL
DataURLと似たような仕組みとして、BlobURLがある。
こちらは、blob:
で始まる文字列。
URL.createObjectURL()
にBlob
を渡すと作成される。
const blob = new Blob(['<xml>foo</xml>'], {type: 'text/xml'}); const url = URL.createObjectURL(blob);
基本的な使い方はDataURLと同じで、これもアドレスバーに入れると確認できる。
ただ、以下の違いがある。
BlobURLは必ずユニークな文字列になる。同一のBlob
を渡しても、その都度、異なるBlobURLが生成される。
そして、これが最大の違いだが、BlobURLの場合は、それ自体がデータを表現しているわけではない。
データはあくまでもブラウザに保存されており、BlobURLはそれにアクセスするためのキーに過ぎない。
データの有効期間は、ブラウザを閉じるまで。
そのため、速度やメモリが、DataURLよりも効率的になる。
その反面、それ自体にデータが入っているわけではないので、BlobURLをサーバーなどに渡してもデータにはアクセスできない。
Chromeの場合、以下のURLで有効なBlobURLを確認できる。
chrome://blob-internals/
使われなくなったBlobURLは自動的に消去されるが、URL.revokeObjectURL()
で明示的に消去するのが望ましいとされる。
URL.revokeObjectURL(url);