javascripttypescriptwebvitewebapi

How to use DecompressionStream to decompress a gzip file using JavaScript in Browser?


All major browsers now support the DecompressionStream API, but I can't figure out how to use it with fetch() to decompress a gzip file in browser.

The following code works with a base64 string:

const decompress = async (url) => {
  const ds = new DecompressionStream('gzip');
  const response = await fetch(url);
  const blob_in = await response.blob();
  const stream_in = blob_in.stream().pipeThrough(ds);
  const blob_out = await new Response(stream_in).blob();
  return await blob_out.text();
};

decompress(
  'data:application/octet-stream;base64,H4sIAAAAAAAAE/NIzcnJVyjPL8pJAQBSntaLCwAAAA=='
).then((result) => {
  console.log(result);
});

However, if I create a hello.txt.gz file using gzip hello.txt on MacOS (hello.txt is a plain text file with content "hello world"), then the function above throws an Error.

decompress('/hello.txt.gz').then((result) => {
  console.log(result);
});
# FireFox
Failed to read data from the ReadableStream: “TypeError: The input data is corrupted: incorrect header check”.
Uncaught (in promise) DOMException: The operation was aborted.

# Chrome
Uncaught (in promise) TypeError: Failed to fetch

# Safari
[Error] Unhandled Promise Rejection: TypeError: TypeError: Failed to Decode Data.
[Error] Unhandled Promise Rejection: TypeError: Failed to Decode Data.

Edit

There is a reproducible demo at Stackblitz.


Solution

  • Thanks to @kaiido's help (see question comments), we found that this error is caused by the Vite server. The development server serve .gz files with wrong headers.

    "content-encoding": "gzip"
    ​​"content-length": "42"
    "content-type": "text/plain"
    

    A workaround is to rename the file to use a different extension (e.g., hello.txt.gzhello.txt.gzip).

    Related Vite Issues