2015-01-13

tarを解凍しつつキャッシュに展開するService worker

というのを書いてみました。なんかたくさんアセットのある例、ってことで emojify.js という絵文字系のJSライブラリのデモページを使ってます。

https://googledrive.com/host/0B8b5YMw-yJleQ3N4bkxrNVlfYjA/

このページはemojify.jsのデモページである http://hassankhan.github.io/emojify.js/ とほとんど同じです。タイトルと注意書きと、service workersの登録を除けば。

ただ、もとのデモページと違って images ディレクトリを消してるので画像ロードが盛大に失敗します。画面スクロールするとサンプル絵文字が読み込み失敗になっていくのが見えるはずです。

ところがChrome 40以降であれば、この時点でservice workerが登録され、動き始めます。service workerは画像ファイルをアーカイブしたimages.tarを取得し、なかのファイルを取り出してキャッシュに突っ込みます。

なので、数秒待ってから(この処理が完了してから)リロードすると、さっきは読み込み失敗してた画像が突然表示されるようになります。しかもキャッシュから取ってくるので速い!という。

書いていて途中で「まあでもコネクションを減らす目的だったらpipeliningしたりspdy使ったりしたほうが効率いいよな……」とふと我に返るタイミングもありましたが、あまり気にしないことに。

---

tarってしょせんヘッダとコンテントが並んでるだけのデータ構造でしょ、って思ってて、詳細しらんけどそれぐらいJSでも読めるんじゃないかなぁ、ということで思いついたのがこのデモのきっかけです。tarのパーサはGNUのマニュアルを見ながら超適当に書きました。

tarは512バイトごとのブロック単位になっており、512バイトのヘッダブロックにそのファイルのメタデータを記述し、必要ならそのファイルのコンテンツを保持したブロックが続きます(ディレクトリやシンボリックリンクなどはコンテントは存在しないのでいきなり次のブロックが続く)。

ファイルの終端は0クリアされたブロックが2つ続くとか、フォーマットやチェックサムの項目があるとか、そういう細々したのがいろいろあるのですが、今回はそういうのは完璧に無視し、ファイル名とファイル長だけを取ってくる単純な仕様にしました。ArrayBufferを使って512バイトごとに区切り、特定のオフセットにアクセスするだけなので簡単でした。

ちょっと驚いたのはサイズのフィールドはASCIIの数字で8進数表記ということですかね。なんでそんな仕様なんでしょうか。