Using DecompressionStream to Unpack Data in JavaScript
The DecompressionStream interface is a built-in web API
in modern JavaScript that decompresses streams of binary data natively
in the browser and Node.js environments. By leveraging the Compression
Streams API, developers can unpack formats like Gzip and Deflate on the
fly without relying on heavy third-party libraries. This article
explains the underlying mechanism of DecompressionStream,
demonstrates how to pipe compressed binary data through it, and outlines
best practices for handling both network responses and in-memory byte
buffers.
How DecompressionStream Works
DecompressionStream implements the standard
TransformStream interface. It sits between a readable
source of compressed bytes and a writable destination, decoding incoming
binary chunks sequentially using native, low-level browser routines
(written in C++ or Rust). Because it operates as a stream, it processes
data chunk-by-chunk rather than loading the entire compressed payload
into memory at once, significantly reducing memory overhead and
latency.
The constructor accepts a format string specifying the compression
algorithm: * 'gzip': For data compressed using the Gzip
format. * 'deflate': For data compressed using the ZLIB
compression format with headers. * 'deflate-raw': For raw
Deflate streams without ZLIB headers or checksums.
Decompressing a Network Stream (fetch)
When downloading compressed assets, you can pipe the HTTP response
body directly into a DecompressionStream. This allows you
to read the unpacked text or binary data as it arrives over the
network.
async function fetchAndDecompress(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// Pipe the raw response stream through the Gzip decompressor
const decompressedStream = response.body.pipeThrough(
new DecompressionStream('gzip')
);
// Consume the decompressed stream as text
const text = await new Response(decompressedStream).text();
return text;
}Decompressing In-Memory Binary Buffers
If you already have compressed binary data stored in an
ArrayBuffer or Uint8Array, you can wrap it in
a Blob or ReadableStream to process it with
DecompressionStream.
async function decompressByteArray(compressedBytes, format = 'gzip') {
// Create a stream from the compressed byte array
const stream = new Blob([compressedBytes]).stream();
// Pipe through the decompression transform stream
const decompressedStream = stream.pipeThrough(
new DecompressionStream(format)
);
// Read the decompressed data into an ArrayBuffer
const decompressedBuffer = await new Response(decompressedStream).arrayBuffer();
return new Uint8Array(decompressedBuffer);
}Reading Chunks Manually with a Stream Reader
For granular control over data processing, you can attach a reader to
the readable side of the DecompressionStream
to process decompressed chunks incrementally.
async function processStreamChunks(compressedStream) {
const decompressionStream = new DecompressionStream('gzip');
const readable = compressedStream.pipeThrough(decompressionStream);
const reader = readable.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
// 'value' is a Uint8Array containing a decompressed data chunk
console.log(`Received decompressed chunk of size: ${value.byteLength}`);
}
}Key Advantages
- Zero External Dependencies: Eliminates the need for
external libraries like
pakoorfflate, reducing JavaScript bundle sizes. - Memory Efficiency: Avoids high memory spikes by unpacking data incrementally rather than buffering whole archives into RAM.
- High Performance: Native engine-level decompression executes significantly faster than equivalent WebAssembly or pure JavaScript decompression routines.