CompressionStream:瀏覽器內建的 gzip

Streams API 的一員,讓前端不用再引 pako 之類的 JS 壓縮庫(幾十 KB,純 JS 也慢)。實作在瀏覽器的 C++ 層(zlib),快很多而且不佔 bundle。

readableStream
  .pipeThrough(new CompressionStream('gzip'))       // 壓縮
  .pipeThrough(new DecompressionStream('gzip'));    // 解壓

支援 'gzip' / 'deflate' / 'deflate-raw'

手動切塊是為了真實的進度回報

即使資料已經全在記憶體裡、可以整塊餵進去,仍然值得自己切成小塊:ReadableStreampull() 只有在下游真的消化完前一塊時才會被呼叫,所以 onProgress 反映的是實際的壓縮進度,而不是「已經丟進 buffer 的量」。這是 pull-based 才有的性質。

const stream = chunkedSource(buffer, onProgress).pipeThrough(new CompressionStream('gzip'));
return new Response(stream).blob();

解壓前先檢查 magic byte

如果 server 回應帶了 Content-Encoding: gzip瀏覽器會自己先解壓,JS 拿到的 Blob 已經是解壓後的;這時再跑一次 DecompressionStream 會丟 incorrect header check。檢查前兩個 byte(0x1f 0x8b)就知道到底解過沒有。

支援度:這通常是這類方案裡最新的相依

Chrome 80(2020)/Safari 16.4(2023-03)/Firefox 113(2023-05)——比 WebAssembly(2017 三大瀏覽器全數落地)晚了六年。所以「這功能需要很新的瀏覽器」的原因往往不是 WASM,而是它。

⚠️ 沒有 feature detection 時,不支援的瀏覽器是當場丟 ReferenceError: CompressionStream is not defined,不是慢慢壞。專案若沒有 browserslist / babel targets 把支援矩陣固定下來,「我們的使用者一定有」就只是一句沒有強制力的註解。