[実用HTML5API]ローカル ストレージへの画像のキャッシュ方法
最近、HTML5 API を使用した機能実装のサンプルを書く機会が増えてきたので、たまったところから数回に別けて書いていこうと思います。
さて今回のネタは、画像を Web ブラウザーの Window オブジェクトが提供する DOM ストレージのローカルストレージに保存する (キャッシュ) 方法についてです。
画像ファイルをキャッシュすることの利点
Web コンテンツでは以前から、写真などのビジュアルなコンテンツはもちろん、文字では表現の難しい内容の説明、あるいはページを装飾の目的で画像が使用されてきました。
時代は進み、回線の速度が向上してくると、コンテンツのリッチ化が進み、それに比例するように使用される画像の量は増える傾向にあります。
また、最近では高精細ディスプレイの普及、画面サイズの大型化などにより、画像のクォリティも高いものが使用されるケースが増えてきています。
しかしながら、回線の速度は常に高い状態を保障されているわけではないため、回線の状態、アプリケーションの構造によっては、画面の読み込みに時間がかかり、なかなか使用できないなど、ストレスを感じることもあるでしょう。
画像ファイルを Web クライアントのローカルストレージに保存し、これを <img> タグから参照させることで、画像の読み込みにかかる時間を軽減させることができます。
ローカルストレージとは?
Web ブラウザーの提供する DOM ストレージの一つで、セッション別のデータまたはドメイン固有のデータを名前/値ペアとしてクライアントに保存できます。
詳しくは、以下のドキュメントをご参照ください。
DOM ストレージの概要
https://msdn.microsoft.com/ja-jp/library/cc197062(v=VS.85).aspx
ローカルストレージに画像データを保存する方法
DOM ストレージが提供するセッションストレージ、ローカルストレージが保存できるのはテキスト形式のデータのみで、画像のようなバイナリーデータは、そのまま保存することができません。
バイナリーデータをテキストデータに変換する方法として Web で一般的に使用されているのが、Base 64 エンコードです。
Base 64 でエンコードした画像データであれば、以下のように <img> タグの src 属性に指定することができます。
<img id="Img1"
alt="" src="data:image/jpg;base64,/9j/
4AAQSkZJRgABAQEASABIAAD/2wBDAAMCAg(略)
vh6oV6laiaWiJaIloi/9k=" />
つまり、Base 64 エンコードされたデータをローカルストレージに保存し、画像を表示する際には、JavaScript を使用して <img> タグの src 属性に保存したデータをセットしてあげれば良いのです。
document.getElementById("pic01").src = window.localStorage.pic01Base64;
ローカルストレージの指定は、以下のように書くこともできます。保存場所の名前が変わる場合は、以下のように書のが良いでしょう。
document.getElementById("pic01").src = window.localStorage[“pic01Base64”];
画像ファイルを JavaScript で Base 64 エンコードするには?
画像ファイルがあらかじめ Base 64 でエンコードされており、JavaSctipt を使用してサーバーサイドから任意に取り出せればスマートなのですが、サーバーサイドの開発にはいささかの工数がかかるのと、状況によってはサーバーサイドの開発が行えない場合もあるでしょう。
そういった場合は、JavaScritp 側で Base 64 エンコードを行う必要がありますが、標準的な JavaScript には Base 64 エンコードを行う機能は用意されていません。
プラグインや、親切な誰かさんが作った OSS の js ライブラリーを使用するのも良いのですが、HTML5 をサポートするモダン Web ブラウザーでは Canvas オブジェクトの toDataURL メソッドを使用して、Base 64 エンコードを行うことができます。
以下は、Canvas オブジェクトの toDataURL メソッドを使用して Base 64 エンコードを行い、ローカルストレージに保存するコードです。
//キャンバスを作成
var canvas = document.createElement("canvas");
//コンテキストを生成
var ctx = canvas.getContext("2d");
//イメージオブジェクトを生成
var image = new Image();
//image オブジェクトに画像が読み込まれた際のイベントハンドラを登録
image.addEventListener("load",
//イベントハンドラ
function () {
//image に読み込んだ画像を描画する
ctx.drawImage(image, 0, 0);
//Canvas から DataURL (Base64 データ) を取り出しローカルストレージに保存
window.localStorage["pic01Cache"] = canvas.toDataURL();
}, false);
//image オブジェクトに画像をロード
image.src = "img/one.png";
画像を表示する際は、以下のように <img> タグの src 属性にローカルストレージの内容を指定すれば OK です。
//<img> タグの SRC に DataURL を指定
document.getElementById("pic01").src = window.localStorage["pic01Cache"];
以下に全体的なサンプルを示します。
<!DOCTYPE html >
<html>
<head>
<title></title>
<script type="text/javascript">
(function () {
//キャッシュをクリアする場合
//window.localStorage.clear();
//ドキュメントのロードが完了した際のイベントを登録
window.addEventListener("load", loadImg, false);
//イベントハンドラ
function loadImg() {
//ローカルストレージに目的のデータがあるか確認
if (window.localStorage["pic01Cache"]) {
//存在すればそれを使用する
document.getElementById("pic01").src
= window.localStorage["pic01Cache"];
document.getElementById("msg").innerText
= "キャッシュされたイメージを使用しました。";
}
else {
//ローカルストレージにデータがない場合
//Canvas を作成
var canvas = document.createElement("canvas");
//描画コンテキストを取得
var ctx = canvas.getContext("2d");
//イメージオブジェクトを生成
var image = new Image();
//image オブジェクトに画像が読み込まれた際のイベントハンドラを登録
image.addEventListener("load",
//イベントハンドラ
function () {
//image に読み込んだ画像を描画する
ctx.drawImage(image, 0, 0);
//Canvas から DataURL (Base64 データ) を取り出し
//ローカルストレージに保存
window.localStorage["pic01Cache"] = canvas.toDataURL();
//<img> タグの SRC に DataURL を指定
document.getElementById("pic01").src
= window.localStorage["pic01Cache"];
}, false);
//image オブジェクトに画像をロード
image.src = "img/one.png";
document.getElementById("msg").innerText
= "イメージをキャッシュしました。";
}
}
})();
</script>
</head>
<body>
<img src="#" alt="Alternate Text" id="pic01"/>
<br />
<div id="msg"></div>
</body>
</html>
画像一つをキャッシュするのにけっこうな行数があるように思われるかもしれませんが、コメントと、行の折り返しの結果そのように見えているだけです。
実際のところ処理自体は大したことはしていません。
また、これを応用して、ページ全体で使用する複数個の画像をキャッシュさせることももちろん可能ですが、読みこんだ画像ファイルを Canvas に描画する (表示はされません) メソッドは、非同期で動作するため工夫が必要です。
実は、複数ファイルをキャッシュするサンプルコードも書いたのですが、ある特定のモダン Web ブラウザーでのみ挙動が安定しないので、掲載を断念しました。非常にだんねんです。
複数ファイルのキャッシュについてはいろいろ工夫してみていただければと思います。