JavaScriptでファイル操作!? File APIを使いこなそう:連載:人気順に説明する初めてのHTML5開発(1/2 ページ)
HTML5では、ローカルのファイルをブラウザ上で直接、取り扱えるようになった。ファイルの読み取りと書き込みを試そう。
powered by Insider.NET
近年のWebアプリケーションでは、画像ファイルやテキスト・ファイル、Officeファイルのアップロードやダウンロードのやり取りが行われることが多くなってきている(例えば、Twitter上での画像ファイル共有やGoogleドキュメントでのOfficeファイルのアップロードなどがそれだ)。
HTML5では、ファイル操作に関するAPIとして「File API」が定義されたことで、ローカルのファイルをブラウザ上で直接、取り扱うことが可能となった。これによって、Webとローカルの違いをアプリケーションで意識しなければならない局面も少なくなる。
現在、File APIは以下の3種類の仕様が策定されている。
名前 | 概要 |
---|---|
File API | ファイルの読み取りを行うAPI |
File API:Writer | ファイルへの書き込みを行うAPI |
File API:Directories and System | ディレクトリ階層内にフォルダやファイルの作成・保存を行うAPI |
File APIの種類と仕様 |
File APIはファイルのプロパティ値や読み込み、書き込み、新規作成などを実施するために全部で約30個のメソッドやプロパティなどを保有している。全てを紹介することが難しいため、今回はFile APIの基本となる読み取りを行うAPIにフォーカスを当てて解説を進める(本稿の後半では書き込みについても部分的に解説する)。
次の表のとおり、現在、リリースされているほとんどの主要ブラウザでFile APIは実装済みだ。
ブラウザ | File API | File API:Writer | File API:Directories and System |
---|---|---|---|
Internet Explorer | 未実装 | 未実装 | 未実装 |
Firefox | 3.5以降 | 6以降*1 | 未実装 |
Chrome | 6以降 | 9以降*2 | 9以降*3 |
Safari | 5以降 | 未実装 | 未実装 |
Opera | 11.1以降 | 未実装 | 未実装 |
主要ブラウザにおけるFile APIの実装状況 *1 インターフェイスの一部に「Moz」というプレフィックスが必要。 *2 インターフェイスの一部に「WebKit」というプレフィックスが必要。 *3 「--unlimited-quota-for-files/--allow-file-access-from-files」というオプションが必要。 |
もちろん全てのメソッドやプロパティが網羅されているわけではないが、今回紹介するFile APIに関してはIE以外のブラウザで実装済みだ(IEでは、次期バージョンとなる10からFile APIが正式対応の予定)。
■File API実装の有無を確認する
File APIの解説に進む前に、File APIの実装の有無を確認するコードを紹介しておこう。File APIの実装の有無は、以下のコードで確認できる。
if (window.File) {
// File APIに関する処理を記述
window.alert("File APIが実装されてます。");
} else {
window.alert("本ブラウザではFile APIが使えません");
}
実行結果は図1のとおりだ。
windowオブジェクトのFileプロパティがある場合はFile APIが利用できるものとして実処理を、ない場合は利用できない旨を伝えるエラー・メッセージの表示などを行うのが一般的だ。
では、順にFile APIの使用方法を確認していく。
■ファイルのプロパティ情報を取得する
まずは、File APIでファイルのプロパティ情報を取得するサンプルを作成する。実行結果は図2のとおり。こちらから実際に試すことができる。
今回は「本連載 第3回:アドインなしで実現可能! ドラッグ&ドロップを使いこなそう」で紹介したDrag and Drop APIを使用して複数のローカル・ファイルをドラッグ&ドロップした際に、ファイル情報を取得できるように実装する。実際のコードは以下のとおりだ。
<script type="text/javascript">
// File API実装チェック
if (window.File) {
window.alert("File APIが実装されてます。");
// File APIが実装されている場合には、dropイベントを登録
document.getElementById("drop").addEventListener("drop", onDrop, false);
} else {
window.alert("本ブラウザではFile APIが使えません");
}
// Drop領域にドロップした際のファイルのプロパティ情報読み取り処理
function onDrop(event) {
// (1)ドロップされたファイルのfilesプロパティを参照
var files = event.dataTransfer.files;
var disp = document.getElementById("disp");
disp.innerHTML = "";
for (var i = 0; i < files.length; i++) {
var f = files[i];
// (2)ファイル名とサイズを表示
disp.innerHTML += "ファイル名 :" + f.name + "ファイルの型:" + f.type + "ファイルサイズ:" + f.size / 1000 + " KB " + "<br />";
}
// (3)ブラウザ上でファイルを展開する挙動を抑止
event.preventDefault();
}
function onDragOver(event) {
// (4)ブラウザ上でファイルを展開する挙動を抑止
event.preventDefault();
}
</script>
<section id="main">
<p>ドラッグアンドドロップで1つから複数のファイルのプロパティを取得します。</p>
<div id="drop" style="width:700px; height:150px; padding:10px; border:3px solid" ondragover="onDragOver(event)" ondrop="onDrop(event)" >ここにドロップしたファイルのプロパティを読み込みます。</div>
<p>ファイルプロパティ表示</p>
<div id="disp" ></div>
</section>
(1)ドロップされたファイルのfilesプロパティを参照
ドロップされたファイルは、dataTransferオブジェクトのfilesプロパティから取得できる。filesプロパティの戻り値は、Fileオブジェクトの配列となる。
(2)ファイル名とサイズを表示
Fileオブジェクトのプロパティから各項目の値を取得し、<div>要素に出力している。
取得できる情報は、以下の表のとおりだ。ファイルの主な情報は、一通り取得できることが確認できるだろう。
名前 | 概要 |
---|---|
name | ファイル名 |
type | ファイルのMIME/TYPE |
size | bytes単位でのファイル・サイズ |
urn | ファイルのURN |
Fileインターフェイスのプロパティ |
(3)(4)ブラウザ上でファイルを展開する挙動を抑止
ブラウザ上でファイルを展開する挙動(=画像ファイルやテキスト・ファイルが開かれて、ブラウザ上に表示されてしまう動作)を防ぐために、preventDefaultメソッドを呼び出す。
従来のHTMLでは、<input>要素で選択したファイルの名前しか取得できなかった。しかし、File APIを使用することで、より多くの情報にアクセスできるようになった。これを利用すれば、(例えば)アップロード前にファイルの事前検証などが実施できるようになるだろう。
■ファイルの内容を取得する
続いて、ファイルの内容を取得するAPIについて解説する。
ファイルを取得するAPIは同期/非同期の2種類が存在する。
名前 | インターフェイス | 概要 |
---|---|---|
同期API | FileReaderSync | ファイルの内容をメソッドの戻り値で取得 |
非同期API | FileReader | ファイルの内容はバックグラウンドで取得し、イベント・ハンドラ経由でファイルの読み取り結果を取得する |
ファイル取得のAPI |
それぞれファイル取得のインターフェイスや呼び出し方法が異なるが、今回は非同期APIを使用したファイル内容の取得について解説する。同期APIは次回解説予定のWeb Workersの中でのみ利用可能なため、非同期APIを利用したファイル内容の取得については次回の記事を確認していただきたい。
FileReaderインターフェイスで利用できる主なメソッド/プロパティは、以下のとおり。
メソッド/プロパティ | 概要 |
---|---|
readAsBinaryString(ファイル) | ファイル内容をバイナリ文字列として返す |
readAsText(ファイル,文字エンコーディング) | 指定された文字エンコーディングでファイルを読み込んで返す |
readAsDataURL(ファイル) | ファイル内容をDataURL(http://www.akanko.net/marimo/data/rfc/rfc2397-jp.txt)の形式で返す |
abord() | ファイルの読み取りを中断 |
result | 各メソッドの結果を参照するプロパティ |
FileReaderのメソッドとプロパティ |
これらを利用して、ファイルの内容を取得してみよう。
先ほどのサンプルと同じようにファイルをWindowsエクスプローラから選択するか、ドラッグ&ドロップでファイルを複数ドロップされた場合にテキスト・ファイルか画像ファイルをFileReaderで読み取り、その内容を表示するサンプルを作成する。実行結果は図3〜5になる。こちらから実際に試せる。
[Note]FileReaderの活用シナリオ
一番イメージしやすい活用シナリオとしては、SkyDrive(次の画面を参照)などのWebアルバム・アプリケーションにおいて、複数の画像を一括アップロードする局面が考えられる。File APIを利用することで、画像ファイルの読み込みなどの実装をプラグインやアドインを利用することなく実装できるようになるだろう(SkyDriveは現在、Silverlightを用いて一括アップロード機能を実装している)。
では、具体的なサンプル・コードを示す。
<script type="text/javascript">
// 前サンプル同様のFile API実装チェック(省略)
// Drop領域にドロップした際のファイルのプロパティ情報読み取り処理
function onDrop(event) {
var files = event.dataTransfer.files;
var disp = document.getElementById("disp");
disp.innerHTML ="";
// ファイルの配列から1つずつファイルを選択
for (var i=0; i< files.length; i++) {
var f = files[i];
// (1)FileReaderオブジェクトの生成
var reader = new FileReader();
// (2)画像ファイルかテキスト・ファイルかを判定
if (!f.type.match('image.*') && !f.type.match('text.*')) {
alert("画像ファイルとテキスト・ファイル以外は表示できません。");
continue;
}
// (3)エラー発生時の処理
reader.onerror = function (evt) {
disp.innerHTML = "読み取り時にエラーが発生しました。";
}
// (4)画像ファイルの場合の処理
if (f.type.match('image.*')) {
// ファイル読取が完了した際に呼ばれる処理
reader.onload = function (evt) {
var li = document.createElement('li');
var img = document.createElement('img');
img.src = evt.target.result;
li.appendChild(img);
li.innerHTML += "<br />";
disp.appendChild(li);
}
// readAsDataURLメソッドでファイルの内容を取得
reader.readAsDataURL(f);
}
// (5)テキスト・ファイルの場合の処理
if (f.type.match('text.*')) {
// ファイル読取が完了した際に呼ばれる処理
reader.onload = function (evt) {
// FileReaderが取得したテキストをそのままdivタグに出力
disp.innerHTML = reader.result;
}
// readAsTextメソッドでファイルの内容を取得
reader.readAsText(f, 'shift-jis');
}
}
// (6)ブラウザ上でファイルを展開する挙動を抑止
event.preventDefault();
}
function onDragOver(event) {
// (6)ブラウザ上でファイルを展開する挙動を抑止
event.preventDefault();
}
</script>
HTML要素部分は前掲のFileAPI.htmの場合と同じなので省略している。また、ドロップされたファイルをdataTransferオブジェクトのfilesプロパティから取得している点も前サンプルと同様だ。以下は、本項固有の部分について、番号順に解説する。
(1)FileReaderオブジェクトの生成
ファイルの内容を読み込むFileReaderオブジェクトを生成している。
(2)画像ファイルかテキスト・ファイルかを判定
画像かテキスト・ファイル以外のファイルが選択された場合に対応していない旨を表示する。そのため、コンテンツ・タイプ(typeプロパティ)からファイル種別を判定している。
(3)エラー発生時の処理
ファイル読み込みでエラーが発生した場合のコールバック関数を、イベント・ハンドラとして登録している。FileReaderに関するイベントは、以下のとおり。
イベント | 概要 |
---|---|
loadstart | 読み込み開始時 |
load | 読み込み成功時 |
loadend | 読み込み終了時(成功/失敗時の双方で発生) |
abord | 読み込み中断時 |
error | 読み込み失敗時 |
progress | 読み込み中 |
FileReaderに関するイベント |
loadイベントとloadendイベントには、「成功時だけに発生する」と「成功時だけでなく失敗時にも発生する」という違いがある。状況に応じて使い分けると良いだろう。
今回はerrorイベントとloadイベントを利用して実装している。
(4)画像ファイルの場合の処理
画像ファイルかどうかを判定し、画像ファイルの場合の処理を記載している。FileReaderにファイルの読み込み成功時に行う処理をloadイベントに登録している。
ポイントはFileReaderのreadAsDataURLメソッドだ。readAsDataURLは画像ファイルを読み込み、リソースへのリンクではなく、データを直接、埋め込むためのスキームであるDataURL形式で返す。読み込んだDataURL形式の結果はresultプロパティで参照されるため、今回は、生成した<img>要素のsrc属性の参照先にFileReaderで読み込んだ結果を指定している。
(5)テキスト・ファイルの場合の処理
基本的な処理の流れは(4)と同様になる。異なる点は、テキスト・ファイルを読み込むためにreadAsTextメソッドを使用しているという点だ。文字コードの指定は第2パラメータで指定している(ここでは「shift-jis」)。テキスト・ファイルはそのまま出力可能なので、<div>要素にinnerHTMLプロパティで追加している。
(6)ブラウザ上でファイルを展開する挙動を抑止
ブラウザ上でファイルを展開する挙動を防ぐために、preventDefaultメソッドを呼び出す。
続いて次のページでは、ファイルに内容を書き込むAPIについて解説する。
Copyright© Digital Advantage Corp. All Rights Reserved.