HTML5のFile API
2021/04/04更新
目次
概要
File APIは、HTML5でブラウザからユーザーのローカルファイルにアクセスしたりするためのAPIである。
ユーザーが、ファイル選択フォームやドラッグ&ドロップなどによって選択したローカルファイルをその場で読み取って表示したり、ブラウザ上でファイルを作成してユーザーにダウンロードさせたりすることができる。
BlobとFile
Blobオブジェクト
Blob
は、巨大なバイナリデータの塊を表すためのクラスである。「Blob」とは「Binary Large Object」に由来する。Blob
オブジェクトは、データそのものであるバッファと、データのContent-Typeの情報を持っている。
Blob
オブジェクトを作成するには、以下のようにする。
const blob = new Blob( [buf1, buf2, …], // バッファの配列 { type: 'text/plain' } // Content-Type );
第1引数の配列で渡したバッファを全て結合したものが、このBlob
のバッファとなる。バッファが1つだけの場合でも必ず配列にして渡す必要があるので注意が必要である。バッファとして指定可能なものは以下があり、混在していてもよい。
ArrayBuffer
または型付き配列(JavaScriptの型付き配列を参照)他の
Blob
文字列(UTF-8のバイト列となる)
第2引数は省略可能で、デフォルトのContent-Typeは空文字列となる。
Blob
オブジェクトは、size
プロパティでデータのサイズ(byte)、type
プロパティでContent-Typeを確認できる。また、slice()
メソッドで一部分の切り出しや、全体の複製ができる。
Fileオブジェクト
File
は、Blob
を継承したクラスで、Blob
の持つ機能に加えて、さらにファイル名と最終更新日時をプロパティとして持たせたものである。
File
オブジェクトを作成するには、以下のようにする。
const file = new File( [buf1, buf2, …], // バッファの配列(Blobと同様) "hoge.txt", // ファイル名 { type: "text/plain", // Content-Type(Blobと同様) lastModified: Date.now() // 更新日時 } });
更新日時は、ミリ秒単位のUnix Timeで、デフォルトでは現在時刻となる。
File
オブジェクトのプロパティは、Blob
と同様のsize
、type
に加えて、name
でファイル名、lastModified
で更新日時を取得できる。
File
オブジェクトは、仮想的なファイル1個を表すオブジェクトである。File
オブジェクトを作成することで、JavaScript上でファイルを作成して、ユーザーにダウンロードさせることなどが可能となる。ユーザーのローカルファイルにアクセスするには、ユーザーが起こしたイベントからFile
オブジェクトを取得する必要があり、詳細は次節で述べる。
Fileの取得方法
ユーザーのローカルファイルを表すFile
オブジェクトは、ユーザーが自らファイルを選択するなどした場合にのみ取得できる(さもなければ、ウェブサイトからローカルファイルにアクセスし放題となり、大変危険な世界になってしまう)。
フォームのファイル選択から取得
フォーム部品の一つであるファイル選択要素(<input type="file">
)のfiles
プロパティを読み取ると、現在選択されているファイルのFile
オブジェクトを束ねたFileList
オブジェクトが取得できる。通常は、FileList
には1つのFile
オブジェクトが入るだけだが、<input>
タグにmultiple
属性を付けて複数ファイルを選択可能にしたときは、FileList
には複数のFile
が含まれている。
// 以下のHTMLによって、ユーザーがファイルを選択したとする。 // <input type="file" id="file_input"> const inputElement = document.getElementById('file_input'); // filesプロパティでFileオブジェクトのリストが得られる const fileList = inputElement.files; for (let i = 0; fileList.length > i; i++) { // ユーザーが選択したファイルのFileオブジェクト const file = fileList[i]; // ファイル名などが得られる console.log(file.name); … }
ブラウザからのファイルアップロードいろいろに使用例を記載しているので、そちらも参照されたい。
ドラッグ&ドロップから取得
HTML要素にドラッグ&ドロップしたときに発生するdrop
イベントのイベントハンドラ内では、ドロップされたファイルのFile
オブジェクトを束ねたFileList
オブジェクトが取得できる。
// ドラッグ&ドロップを受け付ける要素 const dropArea = document.getElementById('drop_area'); // dropイベントのイベントハンドラ dropArea.addEventListener('drop', function(e) { // dataTrasnferプロパティからFileオブジェクトのリストが得られる const fileList = e.dataTransfer.files; for (let i = 0; fileList.length > i; i++) { // ドロップされたファイルのFileオブジェクト const file = fileList[i]; // ファイル名などが得られる console.log(file.name); … } … });
ブラウザからのファイルアップロードいろいろに使用例を記載しているので、そちらも参照されたい。
ファイルの読み出し
FileReaderによる読み出し
File
オブジェクトからその中身を読み出すには、FileReader
クラスを使う。以下の4種類の読み出し方法があり、メソッドを実行して読み出しが完了すると非同期でonload
が呼ばれて、result
プロパティから読み出し結果が取得できる。
メソッド | 説明 | 備考 |
---|---|---|
|
|
|
|
| |
|
| Content-Typeが正しく設定されている必要がある。 |
| バイト値をそのまま文字コードとする文字列として読み出す。 | このような このメソッドの使用は推奨されていない。 |
// FileReaderクラスのインスタンスを作る const reader = new FileReader(); // 読み出し完了時の処理(非同期) reader.onload = function() { // readAs~メソッドによる読み出し結果はresultプロパティで取得できる console.log(reader.result); }; // readAs~メソッドで読み出しを実行する reader.readAsDataURL(file);
FileReader
は、Blob
オブジェクトに対しても使用できる。
新しいメソッドによる読み出し
Promise
の仕様に対応した新しいメソッドも使用できる。text()
では、readAsText()
と異なり文字コードは指定できない(常にUTF-8となる)ので注意が必要である。
// FileReaderのreadAsText(file, 'UTF-8')と同じ file.text().then(function(text) { // textは文字列 console.log(text); … } // FileReaderのreadAsArrayBuffer(file)と同じ file.arrayBuffer().then(function(buffer) { // bufferはArrayBufferオブジェクト const arr = new Uint8Array(buffer); … });
await
キーワードを使うと、非常にシンプルに書くことができる。
const text = await file.text(); const buffer = await file.arrayBuffer();
これらのメソッドはBlob
クラスのものであるので、当然Blob
オブジェクトでも使用できる。
Blob URLスキーム
Blob URLとは
blob:
は新しいURLスキームで、1つのBlob
オブジェクトを一時的に表すためのURLである。URLが使用できる場所ならどこでも使用できるが、人力で作成することはできず、常にJavaScriptで動的に作成する必要がある。
似たものにData URLスキーム(data:
)があるが、Data URLはそのデータの情報を全てURLの中に含んでいるのに対し、Blob URLにはデータ自体は含まれておらず、そのブラウザにおけるメモリ上のBlob
オブジェクトへの参照を表しているようなものとなっている。以下はData URLとBlob URLの比較である。
Data URL | Blob URL | |
---|---|---|
URLの形式 |
|
|
URLが持つ情報 | データそのもの。 | メモリ上のデータ( |
URLの長さ | データのサイズに比例して長くなる。 | 一定の長さ。 |
生成方法・使用方法 | 静的に生成可能で、HTMLに記述したりできる。 | 常に動的に生成し、JavaScriptから使用するしかない。 |
有効期限 | 特に無し。 | ブラウザを閉じるか、明示的に破棄されるまで。 |
一意性 | 全く同じデータなら同じURLとなる。 | 同じ |
Blob URLの作成方法
Blob URLは、URL.createObjectURL()
に対象のBlob
オブジェクトやFile
オブジェクトを渡すだけで作成できる。
// fileは、FileオブジェクトやBlobオブジェクト const blobUrl = URL.createObjectURL(file); // 作成したURLは、そのままHTMLのタグなどに設定できる // 例えば、画像ファイルのFileオブジェクトであれば、以下のように<img>タグに設定して表示できる const img = document.createElement('img'); img.src = blobUrl; document.body.appendChild(img);
Blob URLを作成すると、そのページが閉じられるまでBlob
オブジェクトの内容がメモリ上に置かれる。また、同じBlob
オブジェクトでも、呼び出すたびに新しいURLとなる。これらのことから、必要が無くなったBlob URLは次のようにして明示的に破棄するのが望ましいとされている。
// 作成したBlob URLを破棄する URL.revokeObjectURL(blobUrl);
その他
バイナリー文字列
FileReader
のreadAsBinaryString()
メソッドで登場する「バイナリー文字列」とは、\x00
~\xFF
の256種類の文字だけからなる文字列のことを言う。\x20
~\x7E
はいわゆる通常の半角英数字であるため、半角英数字だけの文字列も「バイナリー文字列」と言えるが、通常これは人間が読む文字列に対してではなく、バイト列をそのまま文字列として扱おうとした場合に必要となってくる考え方である。実際、半角英数字の範囲以外は制御文字や記号付きのアルファベットなどであり、たいていの場合「バイナリー文字列」は文字化けしたような見た目となる。
現在ではArrayBuffer
などバイト列を自由に扱えるクラスが登場したため、バイト列を文字列として取り扱う場面は少なくなっていると思われる。唯一、JavaScriptでBase64変換ができるbtoa()
とatob()
の2つの関数は、入出力がバイナリー文字列となっており、\xFF
を超えるコードポイントの文字(通常の日本語の全角文字など)を与えるとエラーとなってしまう。
バイト値(0~255の数値)をバイナリー文字列にするには、単にString.fromCharCode()
を使って1文字ずつ変換すればよい。逆に、バイナリー文字列からバイト値を得るには、charCodeAt()
を使ってこちらも1文字ずつ変換すればよい。
使用例
ここまで見てきたFile APIを使うと、ユーザーが指定したファイルに対して様々な処理を施したり、新たなファイルを作成してダウンロードさせたりといったことが可能となる。
JavaScriptでファイルを作成してダウンロードする
以下は、簡単なテキストファイルをFile
オブジェクトで作成して、自動的にダウンロードする例である。
// Fileオブジェクトでテキストファイルを作成 const file = new File(['Hello, world!'], 'sample.txt', { type: 'text/plain' }); // Blob URLを作成し、<a>タグにセットする const link = document.createElement('a'); link.href = URL.createObjectURL(file); link.download = file.name; link.innerHTML = 'Download'; document.body.appendChild(link); // <a>タグをクリックして自動的にダウンロードする link.click();
テキストファイルの文字コードを判定・変換する
ユーザーが指定したテキストファイルのFile
オブジェクトを受け取って、そのファイルの文字コードを判定するような関数も作れる。
※以下の例は、ASCII、EUC-JP、UTF-8のいずれか(※Shift_JISは難しいため省略した)をごく簡易的に判定するだけの実装であり、正確に判定できない場合もあるので注意されたい。より実用的には、後述するような既出のライブラリを使うのが良い。
const checkEncoding = function(file) { // fileは、テキストファイルのFileオブジェクト // 判定結果をもって解決するPromiseを返す return new Promise(function(resolve, reject) { const reader = new FileReader(); reader.onload = function() { const bytes = reader.result; let encoding = 'Unknown'; if (/^[\r\n\t\x20-\x7E]*$/.test(bytes)) { encoding = 'ASCII'; } else if (/^(?:[\r\n\t\x20-\x7E]|\x8E[\xA1-\xFE]|\x8F?[\xA1-\xFE][\xA1-\xFE])+$/.test(bytes)) { encoding = 'EUC-JP'; } else if (/^(?:[\r\n\t\x20-\x7E]|[\xC2-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF4][\x80-\xBF]{3})+$/.test(bytes)) { encoding = 'UTF-8'; } resolve(encoding); }; reader.onerror = reject; // 正規表現で判定するため、バイナリー文字列として読み出す reader.readAsBinaryString(file); }); };
文字コードの変換(例えば、UTF-8からShift_JISなど)は簡単にはできないが、バイト列を自由に扱えるので原理的には可能となっている。実際、ブラウザ上で文字コード変換を行なえるライブラリもすでに登場している。例えば、https://github.com/polygonplanet/encoding.jsを使うと、以下のように文字コードの変換が簡単にできる。
const downloadSjisFile = function(file) { // fileは、UTF-8のテキストファイルのFileオブジェクト const reader = new FileReader(); reader.onload = function() { // encoding.jsでShift_JISに変換したバイト列を取得 const sjisArray = Encoding.convert(new Uint8Array(reader.result), 'SJIS', 'UTF8'); // Shift_JISのテキストファイルを作成してダウンロード const sjisFile = new File([new Uint8Array(sjisArray)], 'sjistext.txt', { type: 'text/plain' }); const link = document.createElement('a'); link.href = URL.createObjectURL(sjisFile); link.download = sjisFile.name; link.innerHTML = 'Download'; document.body.appendChild(link); link.click(); }; reader.readAsArrayBuffer(file); });
ファイルのMD5ハッシュ値を出力する
「JavaScriptでMD5を実装してみた」の記事を参照。
外部リンク
全体
BlobとFile
FileReader
Blob URL
その他