🔥

CompressionStream でブラウザで gzip 圧縮する

2024/09/04に公開

モダンなブラウザは JS API として gzip や deflate が使えます。
昔から Chrome に搭載してるのは知ってたんですが、今見たらだいたい搭載してました。

https://developer.mozilla.org/ja/docs/Web/API/DecompressionStream

https://compression.spec.whatwg.org/#decompression-stream

わかってる人向けに言うと、バンドルサイズ大きめの JS実装の pako や zlib.js が不要になって、ブラウザネイティブの(たぶんHTTP上のgzip展開と同等の)高速な実装が使えます。

// impl
const encoder = new TextEncoder();
const decoder = new TextDecoder();

export async function compress(str: string): Promise<ArrayBuffer> {
  const cs = new CompressionStream("gzip");
  const buf = encoder.encode(str);
  const stream = new Response(buf).body!.pipeThrough(cs);
  return new Response(stream).arrayBuffer();
}

export async function decompress(buffer: ArrayBuffer): Promise<string> {
  const ds = new DecompressionStream("gzip");
  const decompressedStream = new Blob([buffer]).stream().pipeThrough(ds);
  const buf = await new Response(decompressedStream).arrayBuffer();
  return decoder.decode(buf);
}

// usage
const json = { a: 1 };
const str = JSON.stringify(json);
const compressed = await compress(str);
const restored = await decompress(compressed);
console.assert(str === restored);

ブラウザ上のJSで gzip できると何が嬉しいかと言うと、なんかバイナリデータを保存する必要がある時に ArrayBuffer を gzip したやつを indexeddb の blob にして突っ込むと、ストレージサイズかなり節約できます。

https://web.dev/articles/indexeddb-best-practices?hl=ja

なんでストレージサイズを小さく保ちたいかと言うと、ブラウザの気分次第ではあるんですが基本的にはストレージ使用量大きいほど消されやすいというのがあります。

https://developer.mozilla.org/ja/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria

サイズが大きくてアクセス頻度が低いほど消されやすい印象。

あとは、S3 や duckdb, parquet, arrow 辺りのAPIと直接喋ったりする時にも、なんか使った記憶があります。

https://duckdb.org/

https://arrow.apache.org/docs/js/

Discussion