2013

8

19

HTML5のFile APIでローカルファイル情報取得してやんよ!!!

Pocket
LINEで送る
Facebook にシェア
reddit にシェア
LinkedIn にシェア

そろそろHTML5 APIのひとつでもリファレンスしていこうかな・・・
かといって今さらcanvasとかやってもしょうがないし、MathMLみたいなマニアックなものは覚えても使う機会があるかどうか・・・という事で

File API』というヤツを手始めに覚えようかと思います。

『File API』について

HTML5以前はブラウザ(またはWebアプリ)からローカルファイルを扱うには<input type="file">タグを利用してユーザーがファイルをアップロードする必要がありましたが、
File APIの登場でJavaScriptからローカルファイルを直接読み取ることができる様になりました。

このAPIはユーザーが指定したローカルファイル(Webブラウザが動作するパソコンのHDDやSSDといったストレージに置かれたファイル)の情報やデータを取得できます。

主なAPIの種類

File APIにはローカルファイルの読み取りだけでなく、書き込みやファイルを操作するAPIも用意されています。

File API

ファイル読み取りを行うAPI

File API:Writer

ファイルへの書き込みを行うAPI

File API:Directories and System

ディレクトリ階層内にフォルダやファイルの作成・保存を行うAPI

取り扱いの注意

書き込み・保存ができる様になったといっても、File APIでアクセスできるのはあくまでユーザーが指定したファイルだけになります。
セキュリティの問題上でパスを「C:\file.txt」や「../text/file.txt」の様に指定して動的に読み込み元ファイルを書き直す事はできません。

主要ブラウザのFile APIへの対応状況

ブラウザ 対応バージョン
Internet Explorer 10.0
Fire Fox 3.6以降
Chrome 6.0以降
Safari 5.1以降
Opera 11.1以降

まぁまず各主要ブラウザの最新版を使っていれば問題無く実装されている様です。

File API対応確認スクリプト

JavaScript
if(window.File && window.FileReader) {
	//File API
	alert("ご使用のブラウザはFile APIを実装しています");
}else{
	alert("ご使用のブラウザはFile APIをサポートしていません");
}

一応こちらのテスト用スクリプトを使えば使用するブラウザがFile APIに対応しているかがわかります。

デモ①ローカルファイルの情報を取得する

情報を取得するファイル選択は<input>タグを使用します。 複数のファイルを選択できる様にmultiple属性を用います。

HTML
<input id="loadFile" type="file" multiple>
JavaScript
function loadFile_changeHandler(e){
	var files = e.target.files;
	var fileData = "";
	for(var i = 0; i < files.length; i++){
		var fileVal = files[i];
		fileData +=
			'ファイル名:' + escape(fileVal.name) + '<br>' +
			'ファイルサイズ:' + fileVal.size + ' バイト<br>' +
			'MIMEタイプ:' + fileVal.type + '<br>' +
			'最終更新日時:' + fileVal.lastModifiedDate + '<hr>';
		}
	$('#info').innerHTML = fileData;
}

function $(id) {
	return document.querySelector(id);
}

これで指定したファイルの「ファイル名」、「ファイルサイズ」、「MIMEタイプ」、「最終更新日時」の各パラメーターが変数filesの配列内に格納されます。

こちらのデモページでローカルファイルを選択してください。

デモ②ファイルの内容を読み取る

ローカルファイルの読み取りにはFile APIの『File Reader』オブジェクトを利用します。

テキストファイルだけでなく、画像などのバイナリファイルも読み取りが可能です。

読み取りメソッド 説明
readAsBinaryString(File) 非同期にファイルを読み取り バイナリ文字列を取得
readAsText(File, enc) 非同期にファイルを読み取り、テキスト文字列を返します。 文字エンコーディングを指定する引数encを省略すると「UTF-8」として読み取ります
readAsDataURL(File) 非同期にファイルを読み取り、Data URIにエンコードされたデータを返します
readAsArrayBuffer(File) 非同期にファイルを読み取り、ArrayBufferオブジェクトとしてデータを返す
HTML
<input id="loadFile" type="file" multiple><br>
<textarea id="info"></textarea>

formタグにて画像のアップロードフォームを定義

JavaScript
	$("#loadFile").onchange = function(evt){
		var file = evt.target.files[0];
		if(!file.type.match(/text/)){
			alert('テキストファイルを' + '選んで下さい');
			return;
	}
	
	var reader = new FileReader();
	reader.onload = function(evt) {
		$("#info").value = evt.target.result;
	}
	
	reader.readAsText(file, "Shift_JIS");
	}
	
	function $(id) {
		return document.querySelector(id);
	}

こちらのデモページでローカルのテキスト形式のファイルを選択してください。

デモ②デモページ解説

  • ①"非同期"でファイルの読み取りが完了するとonloadイベントが発生し、ファイル内容はonloadイベントのイベントハンドラ内で受け取ったデータはtarget.resultプロパティに格納されます。
  • ユーザーがファイルを選択すると、onchangeイベントが発生するので、このイベントを補足するためのイベントハンドラを指定します。
  • ③イベント引き数のtarget.filesプロパティにアクセスして、ファイル情報を調べます。
    MIMEタイプを表すtypeプロパティをチェックして「text」が含まれていないなら読み取れない旨のメッセージを表示します。
  • FileReaderのオブジェクトを作成します。
  • ⑤その後、同オブジェクトでreadAsTextメソッドを実行し、読み取りを開始します。
  • 読み取りが完了した際にはonloadイベントが発生するので、onloadプロパティにイベントハンドラを登録しておきます。
  • ※このイベントハンドラでは、イベント引数のtarget.resultプロパティを参照して、読み取られたテキストを <textarea>に表示します。
  • readAsTextメソッドでは、第2引数に"Shift_JIS"を指定しているので、文字コードがShift_JISのテキストを正しく読み取ることができます。このメソッドは、テキストエンコーディングを自動判定できないので文字コードが合致していないと文字化けを起こします。

デモ③画像のファイルのサムネイルを表示する

FileReaderオブジェクトのreadAsDataURLメソッドを使うと、ファイルを読み取り、URLエンコードされたデータを取得できます。

HTML
<form action="upload.php" method="post" enctype="multipart/form-data">
    <input id="afile" type="file" name="img"><br>
	<img id="thumb" width="320">
    <input id="up_btn" type="submit" value="アップロード" />
</form>        //①
JavaScript
	showImage(false);
	
	$("#afile").onchange = function(evt){
		showImage(false);
		var files = evt.target.files;
		if(files.length == 0) return;
		var file = files[0];
		if(!file.type.match(/image/)) {
			alert('画像ファイルを選んでください');
			return;
		}        //②
		var reader = new FileReader();        //③
		reader.onload = function(evt) {
			$("#thumb").src = reader.result;
			showImage(true);
		}
		reader.readAsDataURL(file);
	}
	
	function showImage(b) {
		var val = b ? "block" : "none";
		$("#up_btn").style.display = val;
		$("#thumb").style.display = val;
	}        //④
	
	function $(id) {
		return document.querySelector(id);
	}

選択した画像のファイルのサムネイルを表示させるデモがこちら

デモ③デモページ解説

  • <form></form>ではPHPスクリプトの専用CGI「upload.php」へ画像ファイルをアップロードを想定し、画像のアップロードフォームを定義。
  • ②スクリプトでははじめに画像とアップロードボタンを非表示
    ユーザーがファイル選択したときのonchangeイベントのために、onchangeプロパティにイベントハンドラを設定し、
    MIMEタイプを調べてファイルが画像かどうか判定します。
  • ③そして、FileReaderオブジェクトのreadAsDataURLメソッドを利用して画像ファイルを読み取ります。
  • ④非同期に読み取り、onloadイベントが発生するとresultプロパティを参照することで読み取られたデータを取得できます。
    その後、<img>タグとアップロードを表示します。

デモ④バイナリファイルを解析する

FilereaderオブジェクトのreadAsBinaryStringメソッドを利用したバイナリファイルの解析は
同メソッドで読み取ったデータはJavaScriptの文字列として取得できます。(1バイトが1文字のデータ)文字列なのでcharCodeAtcharAt等の文字列が持つ各種メソッドを利用できます。

HTML
	<input id="afile" type="file"><br>
	<div id="info"></div><br>
	<img id="thumb" width="320">
JavaScript
	var targetfile = null;
	
	$("#afile").onchange = function(evt){
		var files = evt.target.files;
		if(files.length == 0) return;
		targetFile = files[0];
		var reader = new FileReader();
		reader.onload = readPNGFile;
		reader.readAsBinaryString(targetFile);        //①
	}
	//②
	function readPNGFile(evt) {
		var bin = evt.target.result;
		var sig = String.fromCharCode( 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a);        //③、④
		var head = bin.substr(0, 8);
		if(sig != head) {
			alert("PNGファイルではありません");
			return;
		}
		
		var width = getBinValue(bin, 8 + 0x08, 4);
		var height = getBinValue(bin, 8 + 0x0c, 4);
		var depth = bin.charCodeAt(8 + 0x10);
		
		$("#info").innerHTML =
			"width: " + width + "px<br>" +
			"height: " + height + "px<br>" +
			"depth: " + depth + "bit";
		var reader = new FileReader();
		reader.onload = function(e) {
			$("#thumb").src = reader.result;
		}
		reader.readAsDataURL(targetFile)
	}        //⑤
	
	function getBinValue(bin, i, size) {
		var v = 0;
		for(var j= 0; j < size; j++){
			var b = bin.charCodeAt(i + j);
			v = (v << 8) + b;
		}
		return v;
	}        //⑥
	
	function $(id) {
		return document.querySelector(id);
	}

選択されたPNGファイルをバイナリファイルを解析するデモがこちら

デモ④デモページ解説

  • バイナリデータとしてデータを読み取るためFileReaderオブジェクトのreadAsBinaryStringメソッドを利用します。
  • ②非同期に読み取りを行い、読み取り完了時のonloadイベントでreadPNGFile関数でPNGファイルの解析を行う。
  • ③PNGファイルの先頭8バイトは、PNGファイルを識別するための固定シグネチャとなっています。そのため、冒頭8バイトを調べてPNGファイルかどうか判定しています。
  • String.fromCharCodeメソッドを使うと、特定のバイトからなる文字列を作成できます。これを利用してPNGファイルのシグネチャ文字列を作成しています。
    また、String.substrメソッドを利用すれば、文字列の一部を切り取ることができます。
    文字列を比較することで連続するバイナリがPNGファイルのシグネチャかどうかを判定できます。
  • ⑤PNG画像であるか判明したら、サムネイルを表示し、readAsDataURLメソッドを利用してimgタグのsrc属性に読み取ったデータを設定します。
  • ⑥バイナリデータの解析では、複数バイトからなる数値を読み取る必要があり、これを行うのがgetBinValue関数です。
  • ※PNG画像の幅や高さは、4バイトの数値として記録されるので、同関数を利用して4バイトのバイナリの文字列を、シフト演算などを使って数値に変換しています。
Pocket
LINEで送る
Facebook にシェア
reddit にシェア
LinkedIn にシェア

トップへ