keepcalculator

06 Numbers and Powers

Compress Decompress Files in Node.js

Card No. 06·27682 · Updated · Standard formula, shown below

To compress and decompress files in Node.js, use the built-in zlib module for gzip, deflate, and brotli, or the tar package for archive files. You can pipe a file stream through a compression stream to create a compressed output, and reverse the process to decompress.

Why Compression Matters in Node.js

File compression reduces storage space and speeds up network transfers. In a Node.js server, compressing responses or uploaded files can cut bandwidth costs significantly. For example, a 10 MB JSON log file often compresses to under 1 MB with gzip. Decompression is equally important when receiving compressed data from clients or third-party APIs. Node.js handles both natively without external tools, making it a reliable choice for build scripts, backup utilities, and API middleware.

Built-in zlib Module Overview

The zlib module provides compression algorithms that are also used by web servers and browsers. The most common are:

  • gzip – the standard for HTTP compression; good balance of speed and ratio.
  • deflate – raw deflate stream, often used in WebSocket protocols.
  • brotli – newer algorithm with better compression ratios, especially for text; supported in modern browsers and Node.js 10.16+.

All three are available as promise-based functions, callbacks, and streams. For file operations, streams are recommended because they handle large files without loading everything into memory.

Compressing a File with gzip

Here’s a minimal example that compresses input.txt into input.txt.gz:

const zlib = require('zlib');
const fs = require('fs');

const input = fs.createReadStream('input.txt');
const output = fs.createWriteStream('input.txt.gz');

input.pipe(zlib.createGzip()).pipe(output);

This streams the file in chunks, so memory usage stays flat even for multi-gigabyte files. The same pattern works with createDeflate() or createBrotliCompress().

Decompressing a gzip File

To reverse the process, pipe the compressed file through a gunzip stream:

const zlib = require('zlib');
const fs = require('fs');

const input = fs.createReadStream('input.txt.gz');
const output = fs.createWriteStream('output.txt');

input.pipe(zlib.createGunzip()).pipe(output);

For deflate, use createInflate(); for brotli, use createBrotliDecompress(). Node.js automatically detects the compression format when using the corresponding stream, but you must choose the correct one – there is no universal “auto-detect� stream.

Working with Tar Archives

If you need to compress multiple files or directories into a single archive, use the tar package (the most popular npm package for this). It works with streams and integrates with zlib:

const tar = require('tar');
const fs = require('fs');

// Create a gzipped tar archive
fs.createReadStream('folder')
  .pipe(tar.c({ gzip: true }, ['folder']))
  .pipe(fs.createWriteStream('folder.tgz'));

// Extract a gzipped tar archive
fs.createReadStream('folder.tgz')
  .pipe(tar.x({ cwd: 'extracted' }));

The tar package handles both tar and tar.gz formats. For simple single-file compression, zlib alone is sufficient; for directories or multiple files, tar is the standard approach.

Choosing the Right Algorithm

Your choice depends on the use case:

  • gzip – best for HTTP responses, log rotation, and general file compression. Widely supported everywhere.
  • deflate – slightly faster than gzip but with a larger output; rarely used for files, more for network streams.
  • brotli – best compression ratio for text (HTML, JSON, CSS). Slower to compress but fast to decompress. Use for static assets or API responses where compression time is not critical.

For most file compression tasks, gzip is the safe default. If you control both ends and want maximum space savings, brotli is better. Test with your actual data – compression ratios vary wildly by content type.

Common Pitfalls and Solutions

Several issues frequently trip up developers:

  • Forgetting to handle stream errors – always add on('error') handlers to both input and output streams; otherwise an unhandled error crashes the process.
  • Using the wrong decompression stream – gzip data must use createGunzip(), not createInflate(). They are not interchangeable.
  • Not waiting for the finish event – when reading the output file immediately after compression, listen for the finish event on the output stream or use a promise wrapper.
  • Memory issues with large files – avoid zlib.gzipSync() for files over ~100 MB; use streams instead.

Here’s a more robust version that handles errors and completion:

const zlib = require('zlib');
const fs = require('fs');

function compressFile(inputPath, outputPath) {
  return new Promise((resolve, reject) => {
    const input = fs.createReadStream(inputPath);
    const output = fs.createWriteStream(outputPath);
    const gzip = zlib.createGzip();

    input.pipe(gzip).pipe(output);

    output.on('finish', resolve);
    output.on('error', reject);
    input.on('error', reject);
    gzip.on('error', reject);
  });
}

Performance and Alternatives

For most use cases, zlib is fast enough. If you need even higher speed, consider the zstd (Zstandard) package, which offers better ratios than gzip at similar or higher speeds. However, zstd is not built into Node.js and requires a native module. Another alternative is lz4, which prioritizes speed over ratio, useful for real-time compression. For simple tasks, stick with zlib – it’s zero-dependency, well-tested, and always available.

Frequently Asked Questions

How do I compress a string, not a file?

Use the synchronous or promise-based functions: zlib.gzipSync('hello') returns a Buffer, or await zlib.gzip('hello'). The output is a Buffer, so convert it to base64 or hex if you need a string representation.

Can I decompress a gzip file without knowing its original name?

Yes, gzip streams do not store the original filename. You must specify the output filename yourself. If you need to preserve filenames, use a tar archive, which stores metadata.

Why is my decompressed file different from the original?

This usually happens when you use the wrong decompression method or when the file was corrupted during transfer. Check that you are using the matching gunzip/inflate/brotli stream and verify file integrity with a hash (e.g., SHA-256) before and after.

Is it safe to compress a file while it’s being written?

Yes, but you must ensure the write stream is flushed before reading the compressed output. Using streams with proper finish events handles this automatically.

How do I compress a directory without tar?

You can’t with zlib alone – it only compresses a single stream. Use the tar package to bundle the directory first, then optionally gzip the resulting tar file. That’s the standard approach.

What’s the difference between gzip and zip?

gzip compresses a single stream, while zip is an archive format that stores multiple files and folders. Node.js has no built-in zip support; use the archiver or adm-zip package for zip files.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →