2012/04/17

データベースに格納されている画像データをWebページに表示する

これまでWebアプリケーションを作る際のサムネイルデータなど、サイズのごく限られた画像ファイルをBLOBデータとしてデータベースに格納してきました。こういったケースは比較的よくあると思います。 最近では比較的大きなサイズの画像ファイルでもBLOBとして格納してしまうかもしれません。

今回は比較的サイズの小さな画像データをWebページに大量に貼り付けたい場合のお話しです。

背景

これまでは、DB上にあってファイルとしてアクセスできない画像を表示するために、画像データのストリームを返す小さなWebアプリケーションを準備してきました。

これだと画像ファイルの数が多くなった場合には、画像のデータ分だけのコネクションが必要になります。 keep-aliveが使えて実際のコネクション数が参照数と同じではないとしても、同一ページに小さな画像を大量に表示する事 は全体のパフォーマンス上の懸念材料になってきました。

いままでのベストプラクティスであれば、1ページは30kbyte以下に抑えるとか、画像データの取り扱いについてもいろいろありましたが、デスクトップアプリケーションの代りとしてWebアプリを作る場合には、これまでのルールが当てはまらない場合もでてきました。

HTMLに画像データを埋め込む技術的な話し

これまでの手法だとimgタグのsrc属性に画像表示用のURLを指定してidなどで画像を指定するようにしてきましたが、直接に画像データを指定します。

データを埋め込む例

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAABLCAIAAAA06NSGAAAACXBIWXMAAABI
AAAASABGyWs+AAAACXZwQWcAAAB4AAAASwDhLqRrAAAki0lEQVR42u18a5Bc
..." />

いままで試したプログラミング言語毎の例を載せておきます。

RubyでBase64を扱う方法

いま手元にあるのはRuby 1.9.3-p125ですが、方法は共通のはずで、だいたい次のようなコードになっています。

Base64の文字列を得るのにpackを使っています。

rubyでのイメージファイルのbase64化


## この例ではオリジナルデータをファイルにしています
@filepath = "/somewhere/foo.jpg"

@prefix = "data:$MIME_TYPE;base64,"

## この戻り値をimgタグのsrc属性に指定します。
def getSrcData(data)
  ret = @prefix.sub("$MIME_TYPE", getMimeType())
  ret += [IO.read(@filepath)].pack("m")
  return ret
end

def getMimeType()
  ext = File.extname(@filepath).downcase.sub("\.","")
  ext = "jpeg" if ext =~ /jpg/i
  return format "image/%s", ext
end
JavascriptでBase64を扱う方法

以前、Google Chromeの機能拡張でExifの情報を扱うアプリケーションを作成しました。 Chromeでは同じ機能拡張の中でもメインのタブとポップアップウィンドウの間ではデータ参照が制限されているので、メインコンテンツの画像を機能拡張のポップアップウィンドウの中で表示するには、外部参照のURLを渡すか、データ列を渡すかの方法をとることになります。

その時はExifを解析するためにメインコンテンツ側でダウンロードしたデータをBase64で渡して、ポップアップウィンドウ側に表示させました。これはダウンロード回数を最小にするべきだろうという理由での判断でした。

Javascript自身にはbase64化の手段がないので、Masanao Izumoさんが公開されているbase64.jsを利用しました。

Javascriptでのbase64化


  imgtag_data_list.push("data:image/jpeg;base64,"+base64encode(EXIF.getRawData(node)));

この時はいくつかbase64化のライブラリを試しましたがテキストではなく、バイナリに対応しているのはIzumoさんのコードしか見つけられませんでした。コード中のEXIF.getRawData(node)で参照しているデータはXMLHttpRequestのresponseBodyです。

考慮点

同じ画像を大量に表示するような場合は、テキストファイルに埋め込むよりも、アプリケーション側でLast-ModifiedCache-Controlヘッダーをちゃんと扱うようにするのが最も経済的です。

安易にCGIやらで組むと動的アプリケーションの特性としてCache-Control: no-cache付く事になるので、キャッシュが有効になりませんが、これに気をつけるだけでブラウザが適切に繰り返しの読み込みを効率化してくれます。

他には画像データのサイズが比較的大きい場合は、HTMLファイルのダウンロードに時間がかかる事になるので、最初の画面描画が遅くなる欠点があります。ここ最近のブラウザはレンダリング途中から表示が始まるので、問題にならないかもしれませんが、いずれにしろサイズの大きなファイルを大量に表示するのは、もともとの設計に問題があります。

画像データの埋め込みはあくまでもサムネイルや、HTMLメールなどの用途に限定するべきでしょう。 よくよく考えて使わないと、策に溺れる事になります。

0 件のコメント: