URLシェアを支える技術 lz-string
WebアプリでURLシェアを実装する際に、URLにすべての情報を持たせてしまいたい場合があります。そのとき、情報をそのままクエリ文字列に渡してしまうとURLの文字数制限に引っかかってしまうかもしれません(厳密にはURLに上限はないようですが、現実はいつもブラウザ実装依存)。
そんなときURLセーフな文字列形式で圧縮してくれるライブラリがあります。lz-sringです。
変換の例
ライブラリで compressToEncodedURIComponent
というAPIが提供されているのでこれを使用します。標準のencodeURIComponent
でURLセーフな文字列に変換した場合とサイズ比較をしてみましょう。
import lzstring from "lz-string";
const rawData =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
console.log(encodeURIComponent(rawData).length); // 素のencodeURIComponentの場合: 589文字
const compressed = lzstring.compressToEncodedURIComponent(rawData);
console.log(compressed.length); // lz-stringを使用した場合: 407文字
元のサイズの7割程度まで小さくなっているのがわかります(圧縮対象の文字列によって変わります)。
これならURLに持たせられる情報量を増やすことができますね。
共有先のページでURLから取り出したデータの復元にはdecompressFromEncodedURIComponent
を使用します。
身近なツールで使用されている例
TypeScript Playground
TypeScript Playgroudは簡易的なソースコード共有機能があります。URLにtsconfig.jsonの設定値やソースコードの情報を載せて、URLを開くだけで同じ状態を別のブラウザで復元できるものです。例↓
このソースコード部分の圧縮かつURLセーフな文字列への変換にlz-stringが使用されています。
その該当コードが以下です。
もともとTypeScript PlaygroundがソースコードのURLシェアをどのように実装しているか気になり調べた結果、lz-stringにたどり着きました。
React Compiler Playground
React CompilerもPlaygroundを提供していて、同じくURLシェア機能があります。URLをクリックするだけで共有元と同じソースコードがエディターに入力された状態でWebページが開きます。例↓
このソースコード圧縮にもlz-stringが使用されていることが確認できました。
全然関係ないけどReact Compiler PlaygroundはTypeScriptで実装されているんですね。Flowじゃなかった。
他のAPI
ライブラリはJavaScriptで書かれていますが、.d.ts
ファイルが同梱されています。それを引用。名前と型定義で明らかですね。
compressTo~
で圧縮したデータは同じ形式のdecompressFrom~
で復元できます。
開発者はもともと localStorage
に保存するデータを増やす目的でlz-stringを開発したそうです。
compress
が生成する文字列はinvalidなUTF-16のようで、動作確認はwebkitブラウザのlocalStorage
でしか行われていないようです。compressToUTF16
ならすべてのブラウザでテスト済みとのこと。
本記事冒頭に出てきたLorem ipsum...
サンプルテキストを、戻り値がstring型である関数でそれぞれ圧縮すると次のような結果になります。
console.log(lzstring.compress(rawData).length);
// 153
console.log(lzstring.compressToUTF16(rawData).length);
// 164
console.log(lzstring.compressToBase64(rawData).length);
// 408
console.log(lzstring.compressToEncodedURIComponent(rawData).length);
// 407
compress
が最も圧縮効率が良く、続いてcompressToUTF16
が効率が良いです。compressToBase64
とcompressToEncodedURIComponent
は効率が悪いですが、URLなど文字種に制約がある場合には選択せざるを得ないでしょう。
使い時
- LocalStorageに保存する
開発者がもともと希望していた使い方です。データをJSON.stringify
でstring型にした後compressToUTF16
を使用することで、容量を節約しつつLocalStorageに保存できます。LocalStorageから取り出したらdecompressFromUTF16
で復元しましょう。 - URLの一部にする
compressToEncodedURIComponent
を使用することでURLの一部として利用できる文字列だけを使って圧縮できます。パスパラメーターにも、クエリパラメーターにも、URLハッシュにも安全に使える文字列になります。TypeScriptとReact CompilerのPlaygroundと同じ目的です。 - HTMLの一部にする
リッチテキストエディターなどを開発中、それが吐き出すHTMLにまるっとステートを持たせておきたいことがありました。そのときにcompressToBase64
で変換してdata-
属性にぶち込む方法で乗り切りました。(多分compressToUTF16
でも問題ないが未確認)
注意点
lz-stringは圧縮ツールであり、暗号化や改ざん防止機能は付いていません。圧縮後の文字列は人間可読ではありませんが、平文と変わらないと言えます。
機密データを共有する目的に使ってはいけません。また、decompressFrom~
で復元したデータが意図した形式になっているかのチェックは必要です。
そして、シェアされたURLを復元して画面に表示するならば、クロスサイトスクリプティングの余地ができていないか必ず確認しましょう(lz-stringに限った話ではないですね)。
まとめ
JavaScriptの文字列圧縮ライブラリとしてlz-stringを紹介しました。
大きなデータをURLだけでシェアしたい場合に利用できます。LocalStorageでも使えます。
TypeScriptやReact CompilerのPlaygroundで、ソースコードをURLだけでシェアする機能の実装にlz-stringが使われています。
lz-stringは圧縮ツールであり、暗号化や改ざん防止機能はありません。
それでは!
ちょっと株式会社(chot-inc.com)のエンジニアブログです。 フロントエンドエンジニア募集中! カジュアル面接申し込みはこちらから chot-inc.com/recruit/iuj62owig
Discussion