JavaScriptでFile APIを使ってファイルの中身を読みたい
先日、PHPでサーバへファイルをアップロードする部分について書いたんですが、
あれには、ファイルの形式がきちんと判別できないのでちょっと心配です。
$_FILESにもtype要素でも一応ファイル形式は出るんですが、
これは拡張子からファイル形式を取得してくることがあって信用できない。
というわけで、こちらhttp://kinsentansa.blogspot.jp/2013/04/javascript.htmlを参考に、JavaSciptでファイルのバイナリ形式のヘッダからファイルのMIMEタイプを調べるにはどうすればいいか、を考えてました。
JavaScriptでやりたいのは、送信ボタンを押したときじゃなくて、ファイルを変更したときそのファイルが送っていいかどうかをチェックするにはJavaScriptのイベントハンドラあたりでフォームの値を監視しておくといいのかな、と思ったからです。
まあ、実際MIMEタイプのチェックをどう行うかの結論はリンク先に書いてあるとおりで、大雑把に言うと
- ファイルのフォームを監視して、変化したらFileオブジェクトを取得
- 取得したFileオブジェクトの中身をFileReader.readAsArrayBuffer(Fileオブジェクト)でArrayBufferオブジェクトの形で読み込む
- 読み込んだArrayBufferオブジェクトの先頭のほうをViewで取り出して、ヘッダの最初の方を16進数文字列ないし普通の文字列として取得する
- 取得した16進数文字列ないし普通の文字列に対して、JPG,GIFなど各種ファイル形式に対応する文字列が存在するか.indexOf()でチェックしていく
なんですが、
そもそもFile APIとは?ってぐらいよく分かってなかったので、上で書いた用途に関連する形で、その辺りの
- File APIとは何か
- どうやってローカルデータを受け取るか
- Fileオブジェクトでは何が分かるか
- FileReaderオブジェクトでFileオブジェクトの中身を見る
- バッファとビュー
についてをまとめておきます。
参考にしたのは、
「JavaScript で File API を使用してファイルを読み取る」
http://www.html5rocks.com/ja/tutorials/file/dndfiles/
↑全般的に参考にしました。
「HTMLからイベントハンドラを切り離す!救世主イベントリスナー」
http://akiok-jp.hatenablog.com/entry/2013/04/24/223834
MozillaDeveloperNetworkから
https://developer.mozilla.org/ja/docs/Web/JavaScript/Typed_arrays
https://developer.mozilla.org/ja/docs/Web/JavaScript/Typed_arrays/Int32Array#.E5.B1.9E.E6.80.A7
Internet ExplorerのAPIリファレンスより
http://msdn.microsoft.com/ja-jp/library/ie/br212936(v=vs.94).aspx
です。
File APIとは何か
File APIとは、HTML5に標準的に用意されている、ローカルファイルとやり取りするための標準的な方法で、これを利用すると、
- 画像をサーバに送る前に画像のサムネイルプレビューの作成
- クライアントサイドロジックを使用してアップロードのmymetypeをファイル拡張子と照合
- アップロードのサイズ制限
などができます。つまり、ファイルの中身をJavaScriptで参照することができます。
あ、そういえば、使う前にこちら(http://msdn.microsoft.com/ja-jp/library/ie/br212936(v=vs.94).aspx)の動作確認スクリプトでFILE APIが動作することを確認しておきましょう。
どうやってローカルデータを受け取るか
で、File APIではいくつかローカルファイルを受け取るための形式がいくつかありまして、
(こーいうプログラムやユーザーの操作とプログラムの間をやり取りするファイルの形式や方法はインタフェースって言い方が正式ですが)
- File-単一のファイルを扱うためのオブジェクト。名前、ファイル サイズ、mimetype(主に拡張子から取得)、ファイルハンドラへの参照など読み取り用の情報を提供
- FileList-複数のファイルを扱うためのオブジェクト。Fileオブジェクトが複数並んだ配列です。<input type="file" multiple>ないし、先日のフォームみたいに<form enctype="multipart/form-data">としていたら、こちらになります。
- Blob-ファイルをバイト範囲で区切って扱うためのオブジェクト。
Filelistオブジェクトの場合だと(参考先がこれなので、この記事でもこの場合で進めます)、ファイルを選択するフォームの要素(type="file"のinput要素)をidから取得しておいて、
(type="file"のinput要素).files;
とすると、取得できます。
ファイルが変更されるたびにチェックしたいなら、イベントリスナーやイベントハンドラでこのinput要素を監視しておきましょう。
参考→http://akiok-jp.hatenablog.com/entry/2013/04/24/223834
13/01/03訂正
submit->file submitだったら、送信ボタンじゃないですか……。
Fileオブジェクトでは何が分かるか
Fileオブジェクトは、プロパティを指定するとそのFileオブジェクトが扱っているファイルの基本的なデータを見ることができます。
プロパティの一例
- name → ファイル名。alertなどで表示させる際には、encodeURI,encodeURIComponent関数などで特殊文字をエスケープしておくこと。
- size → バイト単位で表示されるファイルサイズ。
- lastModifiedDate → ファイルの最終更新日。このプロパティだけだと月/日/年 時間(GMT)なので、.toLocalDateString()で年/月/日表記に直したり。
FileReaderオブジェクトでFileオブジェクトの中身を見る
Fileオブジェクトだけでは基本的なデータだけで中身が分からないので、
FileReaderオブジェクトを使って中身を読み込みましょう。
FileReaderオブジェクトでファイルを読み込むためには4つの形式がありますが、
基本的に中身をどの形で受け取るかが違います。
この中身の受け取り方にはバイナリ文字列、文字列、データ形式どおり(サムネイル表示にはこれを用います)、バッファがあります。
こちら https://developer.mozilla.org/ja/docs/Web/JavaScript/Typed_arraysを眺めた結果、キャッシュを思い出したんですが、
とりあえず、バッファ(とビュー)は大きなバイナリデータに効率的にアクセスするために用意されたオブジェクトのようです。
画像データはファイルサイズが大きい可能性があるので、バッファのほうを利用しますので、
FileReader.readAsArrayBuffer(Fileオブジェクト)
を利用します。
バッファとビュー
生のバイナリデータに効率的にアクセスするためのオブジェクトとのバッファとビューですが、理解し切れていないんですが、ひらたく説明すると、
- バッファ=ArrayBufferオブジェクト。データの塊を表すオブジェクトで、特に形式があるというわけではなく、データの塊が入った箱、という感じ。箱を開けるにはビューが必要。
- ビュー=何とかかんとかArrayオブジェクト。何とかの部分には、IntやUintといった配列の要素の型が、かんとかには8,16,32といった一つの要素辺りのビット数が入ります。数字やint,Uintが違ってもプロパティやメソッドは同じです。何とかかんとかと書いていて仕方ないので、以下では一例としてUint8Arrayオブジェクトを使います。
Uint8Array(ArrayBufferオブジェクト)の形でオブジェクトを生成することで、ArrayBufferの中身が何とかかんとかで指定された形式の値に変換されて、Uint8Array()の配列の要素として格納されます。
どこからデータを読み込むか、どれだけの長さを読み込むか、といったことを指定することもできます。
配列の要素の取得にはgetメソッドあるいは直接indexを指定すれば、その部分のデータに当たる整数値が得られます。
ビューの配列の要素(整数値)として、バッファの中に入っていたデータを取り出したら、後はそれをtoStringなりfromCharCodeなりを使って文字列に変換すれば、ヘッダに含まれているファイルタイプを調べるのに必要な文字列がゲットできます。
最後に、
ねんがんのもじれつをてにいれたぞ!