[HTML5] FileSystem APIの概要と活用 #2

画像ファイルの読込みと書込み

前回、FileSystem APIの読込と書込みの概要について説明をしました。今回はそれの活用方法について考えてみたいと思います。良くあるサムネイル画像一覧から画像をクリックすると大きな画像が表示されるページを作成します。表示する大きな画像は画面表示時に全てサーバから取得し、ローカルに保存します。そして保存したファイルを画面に表示するようにします。ローカルにファイルが既に保存されている場合はサーバから画像は取得せず、ローカルの画像をそのまま表示するようにします。
画像を表示するのに今回はLightBoxを使用します。
LightBoxはここからダウンロードして下さい。使用したバージョンは2.6になります。またブラウザはchromeのバージョン31を使用しています。

ファイル構成

ダウンロードしてきたLightBoxを解凍し、そのまま「CSS」「js」「img」を任意のフォルダに置いて使用します。 また、LightBoxに付属されているサンプルの画像はimage1~3まで使用し、それぞれファイルをリサイズしファイルサイズの小さい画像を用意しています。view.htmlはこれから作成するhtmlファイルになります。

ディレクトリ構成

 LightBoxを使用する

まずは普通にLightBoxを使用して動かしてみましょう。
CSS及びjsを読み込む

<link href="css/lightbox.css" type="text/css" rel="stylesheet" media="all" />
<script src="js/jquery-1.10.2.min.js" type="text/javascript"></script>
<script src="js/lightbox-2.6.min.js" type="text/javascript"></script>

読込むファイルを指定する。
読込む画像の配列の数だけ繰り返し処理を行い、サムネイル及びリンクを作成します。

// スライド用画像
imageArray =[
    "image-1.jpg",
    "image-2.jpg",
    "image-3.jpg"
];

// サムネイル用画像
imageArray2 =[
     "image-1_small.jpg",
     "image-2_small.jpg",
     "image-3_small.jpg"
];
// スライド用画像のリンクを返却
function assignUrl(img_num) {
    return "http://localhost/img/demopage/" + imageArray[img_num];
}
// サムネイル画像のリンクを返却
function assignUrlSmall(img_num) {
    return "http://localhost/img/demopage/" + imageArray2[img_num];
}

$(function() {
    // 画像の数だけサムネイルを作成する
    for (var i = 0; i < imageArray.length; i++){
        // サムネイル画像をクリックした時のリンクを作成
        var div = $("<a/>").attr("href",assignUrl(i))
                    .attr("id","img" + i).attr("rel","lightbox[imagegroup]");
        // サムネイル画像を作成
        div.append($("<img/>").attr("src",assignUrlSmall(i))
                    .attr("width","10%").attr("height","10%").attr("hspace","5"));
        $("body").append(div);
    }
});

これでまずは普通にLightBoxを使用した画面が作成できました。

サムネイル
サムネイルをクリックするとこんな感じで表示されます。
ライトボックス

画像ファイルの呼出し

「HTML5 FileSystem APIの概要と活用 #1」で説明した方法で画像ファイルをローカルから呼び出します。サムネイル画像をクリックした時のリンク部のソースを書き換え、ファイルを読込むメソッドを呼び出します。(assignUrl⇒readFile)

$(function() {
    // 画像の数だけサムネイルを作成する
    for (var i = 0; i < imageArray.length; i++){
        // サムネイル画像をクリックした時のリンクを作成
        var div = $("<a/>").attr("href",readFile(imageArray[i], "img" + i, assignUrl(i)))
                    .attr("id","img" + i).attr("rel","lightbox[imagegroup]");
        // サムネイル画像を作成
        div.append($("<img/>").attr("src",assignUrlSmall(i))
                    .attr("width","10%").attr("height","10%").attr("hspace","5"));
        $("body").append(div);
    }
});

ここで呼び出しているreadFileメソッドはこんな感じになります。

function readFile(fileName, id, href) {
    window.webkitRequestFileSystem(window.PERSISTENT, 1024*1024, function(fs) {
        // ファイルを取得する
        fs.root.getFile(fileName, {create : false}, function(fileEntry) {
            // filesystem: URL を取得し、hrefに設定する。
            $("#" + id).attr("href", fileEntry.toURL());
        }, function() {
            // ファイルが存在していなかったので、サーバから画像ファイルを取得する。
            var oReq = new XMLHttpRequest();
            oReq.open("GET", href, true);
            oReq.responseType = "arraybuffer";

            oReq.onload = function(oEvent) {
              var blob = new Blob([oReq.response], {type: "image/jpg"});
              // 画像ファイルをローカルに書き込む
              writeFile(id, fileName, blob);
            };

            oReq.send();
        });
     });
}

まずファイルが保存されているかを調べます。ファイルが保存されているかは、fs.root.getFileで取得する際に「create:false」にすることでファイルが無ければ指定したエラーハンドラが呼び出されます。ここではエラーハンドラ内でXMLHttpRequestを使ってサーバから画像ファイルを取得しています。
既にファイルが存在している場合はリンクのhrefにURLを設定します。

// filesystem: URL を取得し、hrefに設定する。
$("#" + id).attr("href", fileEntry.toURL());

FileSystem API には、filesystem: という新しい URL スキームがあります。これをhref属性に指定すればローカル保存したファイルが読み込まれます。具体的なURLはこんな感じになります。「filesystem:http://localhost/persistent/image-1.jpg 」

画像ファイルの保存

サーバからファイルが取得出来たらwriteFileメソッドを呼出し、画像をローカルに保存します。
readFileメソッドから呼び出されるwriteFileメソッドはこんな感じになります。書き込みについては「HTML5 FileSystem APIの概要と活用 #1」で説明した内容とほとんど一緒です。書き込むためのBlobオブジェクトはreadFileメソッドから渡されるので、それをそのまま書き込んでいます。

function writeFile(id, name, blob) {
    var imageData = blob;
    navigator.webkitPersistentStorage.requestQuota(1024*1024, function(bytes) {
        window.webkitRequestFileSystem(window.PERSISTENT, bytes, function(fs) {
           fs.root.getFile(name, {create: true}, function(entry) {
               fileEntry = entry;
               fileEntry.createWriter(function(fileWriter) {
                   fileWriter.onwriteend = function(e) {
                       console.log('書き込み完了');
                   };
           
                   fileWriter.onerror = function(e) {
                       console.log('書き込みエラー: ' + e.toString());
                   };

                   fileWriter.write(imageData);

                   // filesystem: URL を取得し、hrefに設定する。
                   $("#" + id).attr("href", fileEntry.toURL());
               });
            });
        });
    });
}

実行してみましょう

画面を表示させるとまず、ローカルに画像を保存します。サムネイル画像をクリックした時の大きい画像はローカルから呼び出された画像になります。一見ファイルがローカルから読み取られているか分からないかと思いますが、サムネイル画像上で右クリックし、「リンクアドレスをコピー」を選択し、別ウィンドウに貼り付けてみてください。URLが「filesystem:http:~」になっているのが分かります。

まとめ

今回は画像をローカルファイルに保存し、そのファイルを読込み表示させてみました。ローカルに保存したファイルがhref属性に指定できるように変換できるのはとても便利だったと思います。仕様が正式に固まるのはもう少しかかるかと思いますが、是非使ってみてください。