html5のファイルAPIについて調べた。http://www.w3.org/TR/FileAPI/ を読みながら、メモ。やっぱり本家ドキュメントは頼りになるのね。
FileAPIはなにか
- ローカルファイルにアクセスできる。
- アクセスできるローカルファイルはinput で指定された物だけ。*1
使い方
input に files タイプを指定するとFileAPIの対象になる。
<input type=files >
filesタイプの読み込み方
file = document.forms[0].elements[0].files[0]
このように、<input type=files/>へJS経由でアクセスできる。
input type=filesの項目は、複数ファイルの配列になっている。FileListオブジェクト呼ぶ
FileList オブジェクト
通常の配列みたいなオブジェクトして定義されている。つまり配列だと思っておけば良い。
files = document.forms[0].elements[0].files file.length files[0] files.item(0)
File。FileListの中に入ってるオブジェクト
input type=filesでユーザーが指定したファイルのオブジェクトが入っている。
File/FileListは、Iteratorパターンの関係にあるの
Fileオブジェクトでとれる物。
- ファイル名
- サイズ
- 最終更新日
files = document.forms[0].elements[0].files file = files[0] file.name // => "test.xls" files.lastmodifideDate // => "" files.size // => 1234567
ただし、内容読み込みは出来ない。
なぜか?
input type=fileで、ブラウザがファイルを読み込んでたら遅いジャン
だったらどうするの?
ローカルファイルすら非同期で読み込みます。
File.read メソッドって無いけど?
ないよ。
Fileは FileReader()をつかって非同期に読みこみます。 File は file://のURLを示すオブジェクトなんだ。
非同期で読み込むって?
XHRみたいに読み込めば良いんだよ。
new XmlHttp()のかわりに、new FileReader()する
何度も言うけど、非同期だからね!!
reader = new FileReader() file = document.forms[0].elements[0].files[0] reader.readAsText(file, 'UTF-8')#このあとはEvent処理に任せるよ!
非同期なんだから、Event処理を書いとかないとしょうが無い。
reader = new FileReader() reader.onload = function(evt){} //読み込み完了したら呼ばれる。 reader.onprogress = function(evt){} //ファイル読み込み中に呼ばれる。 reader.onerror = function(ev){} //エラー時に呼ばれる。 reader.readAsText(file, 'UTF-8')#Event処理に任せるよ!
イベントにはどんな種類があるの?
Filereaderが持ってるイベントはこんなのがある
ハンドラ名 | イベント | タイミング |
---|---|---|
onloadstart | loadstart | データ読み込み開始時点 |
onprogress | progress | データを読み込んだタイミングで |
onabort | abort | 強制停止(abort()関数)で止めた時点 |
onerror | error | リクエストがエラーだった(404とか) |
onload | load | リクエスト成功時 |
onloadend | loadend | リクエスト完了時(エラーでも成功でも関係なく、送信が完了した時点で呼ばれる) |
イベントは以上です。
ぐだぐだ言わずに、ファイル読めりゃ良いんだよ
僕らはファイルを読み込みたいだけなんだ。
reader = new FileReader() file = document.forms[0].elements[0].files[0] reader.readAsText(file, 'UTF-8')#このあとはEvent処理に任せるよ! ///しばらく待つ。 reader.readyState //これが2になれば読み込みおわり while(reader.readyState!=2){ } //ファイルの中身を見る reader.result
readyStateとか見覚えあるよね
そうXHRみたいなもんなんだよ。
つまり
比較してみよう
データ | オブジェクト | |
Ajax | URL:アドレス | Xmlhttp |
FileAPI | Fileオブジェクト | FileReader |
対で覚えるとイメージしやすいみたい。
何で非同期になってるのよ。
ファイル読み込み程度で、ほかのJSの動作をストップさせるともったいない。だからマルチスレッドで動くようにファイル読み込みを別スレッドにしてる。HTMLを読み込んでDOMを構築するときにIMGやCSSを非同期に読み込むでしょ?あれと同じことをローカルファイルでもやってる。
ってことみたい
絶対に同期できないの?
もちろん出来る。
XMLHTTPに同期通信が用意されているように、ファイルにも同期が用意されている。それが FileReaderSync
全部非同期で良いジャン?
いや、WebWorkersでプログラマがスレッド作れるから、スレッド同期処理はあった方が良い。
部分的にファイル読込できないの?
HTTPはファイルの部分取得が出来る、FileAPIは出来ないの?
もちろん出来る。File#sliceがそれ。
まとめ
HTTPとおなじ扱いのFILEとして定義されている。
同期・非同期、部分読み込みがあるのです。やっぱHTTPと同じだよね。Fileだからといっても、思想は何も変わらない。オブジェクトが変わるだけ。
ところで、読み込んだファイルをどう使うの?
画像をインラインに埋め込む記述を覚えていますか?
<img src="data:123456yjgbvd”/>
ファイルAPIで読み込んだファイル内容は、
<img src=“blob:123456yjgbvd”/>
と書き込み、使うことが出来る。
data: だと base64エンコードが必要だからめんどくさいけど、blogならバイナリそのままで楽ちん。
これをblob: プロトコルと呼びます。
講釈は良いから、使い方を
説明多くてすいません。こうやります。
var file = document.getElementById('file').files[0]; if(file){ blobURLref = window.URL.createObjectURL(file); myimg.src = blobURLref; }
1:window.URL.createObjectURL() にファイルを渡すと blob:xxxxxのURLが帰ってきます。
2:blob:のURLをimg.srcに埋め込めば終わり。
簡単ですね。
追記:window.URLについて 2012-08-06
chrome/safariの Webkitでは、window.URLがベンダープレフィックス付きでないとアクセス出来ない場合もあります。
window.webkitURL
などとベンダープレフィックス webkitをつけてくださいな。
追記 blob URL生成はもっと簡単な方法がありました。
var file = document.getElementById('file').files[0]; var reader = new FileReader(); reader.readAsDataURL(file) //暫く待つ while(reader.readyState!=2){ } img.src = reader.result ;
追記の追記。Android4.0では readAsDataURLがうまく動作しなかった。window.URL.createObjectURL(file)を使うのが無難
追記 BLOBについて:2012/08/06
意図的にBlobを外してたけど、やっぱり書いた方がいいとおもうので追記
FileAPIは file://みたいなもんだと言いました。ネットの先にあるのがhttp、ローカルファイルにあるのがfile、ではHTML中たとえばCanvasなどに入ってるデータは何になるのか?それがblobです。 canvsなどのメモリに載ってるデータはBLOBです。読み込んだデータもHTML中のimgタグなどに存在するのでBlobです。
プロトコル | 書式 | |
HTTP(s) | http:// | |
File | file:// | |
Blob | blob:// |
みたいなもんです。
blobは、読み込んだデータそのものを表す。
Fileは Blobの拡張で定義されてる、なぜか?ファイルを読み込んだら、バイナリが出てくるからに他かならない。
出てきたバイナリはそのままCanvasに入れるなり、Imageにいれるなり、Videoで再生するなどになる。
*1:イベントからも取れるけど、まずはinput から抑えるといいよ