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(), notcreateInflate(). They are not interchangeable. - Not waiting for the finish event – when reading the output file immediately after compression, listen for the
finishevent 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
- Assess an AI-Generated Vulnerability Finding
- AI Chatbot Prompt Tools: How They Work and When to Use Them
- Pihole Unsecured: Browsing History Exposure Risk?2,400 queries
- Apple Smart Lists: What They Are & How to Use Them5.00 min/km
- Old Raspberry Pi Tech Hobbies to Start5.00 min/km
- T-Mobile Home Internet Data Throttling Rule for New Customers