Node.js v26.10.0 documentation
- Node.js v26.10.0
- Table of contents
- Zlib
- Threadpool usage and performance considerations
- Compressing HTTP requests and responses
- Memory usage tuning
- Flushing
- Constants
- Class:
Options - Class:
BrotliOptions - Class:
zlib.BrotliCompress - Class:
zlib.BrotliDecompress - Class:
zlib.Deflate - Class:
zlib.DeflateRaw - Class:
zlib.Gunzip - Class:
zlib.Gzip - Class:
zlib.Inflate - Class:
zlib.InflateRaw - Class:
zlib.Unzip - Class:
zlib.ZipBuffernew zlib.ZipBuffer(buffer)zipBuffer.add(filename, data[, options])zipBuffer.addSync(filename, data[, options])zipBuffer.addEntry(entry)zipBuffer.clear()zipBuffer.commentzipBuffer.delete(name)zipBuffer.entries()zipBuffer.forEach(callback[, thisArg])zipBuffer.get(name)zipBuffer.has(name)zipBuffer.keys()zipBuffer.sizezipBuffer.toBuffer([options])zipBuffer.toBufferSync([options])zipBuffer.values()zipBuffer.writable
- Class:
zlib.ZipEntry- Static method:
zlib.ZipEntry.create(filename, data[, options]) - Static method:
zlib.ZipEntry.createStream(filename, source[, options]) - Static method:
zlib.ZipEntry.createSymlink(filename, target[, options]) - Static method:
zlib.ZipEntry.createSync(filename, data[, options]) - Static method:
zlib.ZipEntry.read(buffer) zipEntry.commentzipEntry.compressedzipEntry.compressedSizezipEntry.content([options])zipEntry.contentSync([options])zipEntry.contentIterator([options])zipEntry.crc32zipEntry.flagszipEntry.isDirectoryzipEntry.isFilezipEntry.isSymlinkzipEntry.modezipEntry.modifiedzipEntry.methodzipEntry.namezipEntry.nameBufferzipEntry.rawContentzipEntry.size
- Static method:
- Class:
zlib.ZipFile- Static method:
zlib.ZipFile.open(filename[, options]) - Static method:
zlib.ZipFile.openSync(filename[, options]) zipFile.add(filename, data[, options])zipFile.addEntry(entry)zipFile.addEntrySync(entry)zipFile.addSync(filename, data[, options])zipFile.close()zipFile.closeSync()zipFile.commentzipFile.compact([comment])zipFile.compactSync([comment])zipFile.delete(name)zipFile.deleteSync(name)zipFile.entries()zipFile.entriesSync()zipFile.forEach(callback[, thisArg])zipFile.forEachSync(callback[, thisArg])zipFile.get(name)zipFile.getSync(name)zipFile.has(name)zipFile.keys()zipFile.sizezipFile.stream(name[, options])zipFile.values()zipFile.valuesSync()zipFile.writable
- Static method:
- Class:
zlib.ZlibBase - Class:
ZstdOptions - Class:
zlib.ZstdCompress - Class:
zlib.ZstdDecompress zlib.constantszlib.crc32(data[, value])zlib.createBrotliCompress([options])zlib.createBrotliDecompress([options])zlib.createDeflate([options])zlib.createDeflateRaw([options])zlib.createGunzip([options])zlib.createGzip([options])zlib.createInflate([options])zlib.createInflateRaw([options])zlib.createUnzip([options])zlib.createZipArchive(entries[, options])zlib.createZipArchiveSync(entries[, options])zlib.zipFiles(files[, options])zlib.createZstdCompress([options])zlib.createZstdDecompress([options])zlib.getMaxZipContentSize()zlib.setMaxZipContentSize(size)- Convenience methods
zlib.brotliCompress(buffer[, options], callback)zlib.brotliCompressSync(buffer[, options])zlib.brotliDecompress(buffer[, options], callback)zlib.brotliDecompressSync(buffer[, options])zlib.deflate(buffer[, options], callback)zlib.deflateSync(buffer[, options])zlib.deflateRaw(buffer[, options], callback)zlib.deflateRawSync(buffer[, options])zlib.gunzip(buffer[, options], callback)zlib.gunzipSync(buffer[, options])zlib.gzip(buffer[, options], callback)zlib.gzipSync(buffer[, options])zlib.inflate(buffer[, options], callback)zlib.inflateSync(buffer[, options])zlib.inflateRaw(buffer[, options], callback)zlib.inflateRawSync(buffer[, options])zlib.unzip(buffer[, options], callback)zlib.unzipSync(buffer[, options])zlib.zstdCompress(buffer[, options], callback)zlib.zstdCompressSync(buffer[, options])zlib.zstdDecompress(buffer[, options], callback)zlib.zstdDecompressSync(buffer[, options])
- Iterable Compression
compressBrotli([options])compressBrotliSync([options])compressDeflate([options])compressDeflateSync([options])compressGzip([options])compressGzipSync([options])compressZstd([options])compressZstdSync([options])decompressBrotli([options])decompressBrotliSync([options])decompressDeflate([options])decompressDeflateSync([options])decompressGzip([options])decompressGzipSync([options])decompressZstd([options])decompressZstdSync([options])
- Zlib
- Index
- About this documentation
- Usage and example
- Assertion testing
- Asynchronous context tracking
- Async hooks
- Benchmark runner
- Buffer
- C++ addons
- C/C++ addons with Node-API
- C++ embedder API
- Child processes
- Cluster
- Command-line options
- Console
- Crypto
- Debugger
- Deprecated APIs
- Diagnostics Channel
- DNS
- Domain
- Environment Variables
- Errors
- Events
- File system
- FFI
- Globals
- HTTP
- HTTP/2
- HTTPS
- Inspector
- Internationalization
- Iterable Streams API
- Modules: CommonJS modules
- Modules: ECMAScript modules
- Modules:
node:moduleAPI - Modules: Packages
- Modules: TypeScript
- Net
- OS
- Path
- Performance hooks
- Permissions
- Process
- Punycode
- Query strings
- Readline
- REPL
- Report
- Single executable applications
- SQLite
- Stream
- String decoder
- Test runner
- Timers
- TLS/SSL
- Trace events
- TTY
- UDP/datagram
- URL
- Utilities
- V8
- Virtual File System
- VM
- WASI
- Web Crypto API
- Web Streams API
- Worker threads
- Zlib
- Other versions
- Options
Zlib#
Stability: 2 - Stable
The node:zlib module provides compression functionality implemented using
Gzip, Deflate/Inflate, Brotli, and Zstd.
To access it:
import zlib from 'node:zlib';const zlib = require('node:zlib');
Compression and decompression are built around the Node.js Streams API.
Compressing or decompressing a stream (such as a file) can be accomplished by
piping the source stream through a zlib Transform stream into a destination
stream:
import { createReadStream, createWriteStream, } from 'node:fs'; import process from 'node:process'; import { createGzip } from 'node:zlib'; import { pipeline } from 'node:stream'; const gzip = createGzip(); const source = createReadStream('input.txt'); const destination = createWriteStream('input.txt.gz'); pipeline(source, gzip, destination, (err) => { if (err) { console.error('An error occurred:', err); process.exitCode = 1; } });const { createReadStream, createWriteStream, } = require('node:fs'); const { createGzip } = require('node:zlib'); const { pipeline } = require('node:stream'); const gzip = createGzip(); const source = createReadStream('input.txt'); const destination = createWriteStream('input.txt.gz'); pipeline(source, gzip, destination, (err) => { if (err) { console.error('An error occurred:', err); process.exitCode = 1; } });
Or, using the promise pipeline API:
import { createReadStream, createWriteStream, } from 'node:fs'; import { createGzip } from 'node:zlib'; import { pipeline } from 'node:stream/promises'; async function do_gzip(input, output) { const gzip = createGzip(); const source = createReadStream(input); const destination = createWriteStream(output); await pipeline(source, gzip, destination); } await do_gzip('input.txt', 'input.txt.gz');const { createReadStream, createWriteStream, } = require('node:fs'); const { createGzip } = require('node:zlib'); const { pipeline } = require('node:stream/promises'); async function do_gzip(input, output) { const gzip = createGzip(); const source = createReadStream(input); const destination = createWriteStream(output); await pipeline(source, gzip, destination); } do_gzip('input.txt', 'input.txt.gz') .catch((err) => { console.error('An error occurred:', err); process.exitCode = 1; });
It is also possible to compress or decompress data in a single step:
import process from 'node:process'; import { Buffer } from 'node:buffer'; import { deflate, unzip } from 'node:zlib'; const input = '.................................'; deflate(input, (err, buffer) => { if (err) { console.error('An error occurred:', err); process.exitCode = 1; } console.log(buffer.toString('base64')); }); const buffer = Buffer.from('eJzT0yMAAGTvBe8=', 'base64'); unzip(buffer, (err, buffer) => { if (err) { console.error('An error occurred:', err); process.exitCode = 1; } console.log(buffer.toString()); }); // Or, Promisified import { promisify } from 'node:util'; const do_unzip = promisify(unzip); const unzippedBuffer = await do_unzip(buffer); console.log(unzippedBuffer.toString());const { deflate, unzip } = require('node:zlib'); const input = '.................................'; deflate(input, (err, buffer) => { if (err) { console.error('An error occurred:', err); process.exitCode = 1; } console.log(buffer.toString('base64')); }); const buffer = Buffer.from('eJzT0yMAAGTvBe8=', 'base64'); unzip(buffer, (err, buffer) => { if (err) { console.error('An error occurred:', err); process.exitCode = 1; } console.log(buffer.toString()); }); // Or, Promisified const { promisify } = require('node:util'); const do_unzip = promisify(unzip); do_unzip(buffer) .then((buf) => console.log(buf.toString())) .catch((err) => { console.error('An error occurred:', err); process.exitCode = 1; });
Threadpool usage and performance considerations#
All zlib APIs, except those that are explicitly synchronous, use the Node.js
internal threadpool. This can lead to surprising effects and performance
limitations in some applications.
Creating and using a large number of zlib objects simultaneously can cause significant memory fragmentation.
import zlib from 'node:zlib'; import { Buffer } from 'node:buffer'; const payload = Buffer.from('This is some data'); // WARNING: DO NOT DO THIS! for (let i = 0; i < 30000; ++i) { zlib.deflate(payload, (err, buffer) => {}); }const zlib = require('node:zlib'); const payload = Buffer.from('This is some data'); // WARNING: DO NOT DO THIS! for (let i = 0; i < 30000; ++i) { zlib.deflate(payload, (err, buffer) => {}); }
In the preceding example, 30,000 deflate instances are created concurrently. Because of how some operating systems handle memory allocation and deallocation, this may lead to significant memory fragmentation.
It is strongly recommended that the results of compression operations be cached to avoid duplication of effort.
Compressing HTTP requests and responses#
The node:zlib module can be used to implement support for the gzip, deflate,
br, and zstd content-encoding mechanisms defined by
HTTP.
The HTTP Accept-Encoding header is used within an HTTP request to identify
the compression encodings accepted by the client. The Content-Encoding
header is used to identify the compression encodings actually applied to a
message.
The examples given below are drastically simplified to show the basic concept.
Using zlib encoding can be expensive, and the results ought to be cached.
See Memory usage tuning for more information on the speed/memory/compression
tradeoffs involved in zlib usage.
// Client request example import fs from 'node:fs'; import zlib from 'node:zlib'; import http from 'node:http'; import process from 'node:process'; import { pipeline } from 'node:stream'; const request = http.get({ host: 'example.com', path: '/', port: 80, headers: { 'Accept-Encoding': 'br,gzip,deflate,zstd' } }); request.on('response', (response) => { const output = fs.createWriteStream('example.com_index.html'); const onError = (err) => { if (err) { console.error('An error occurred:', err); process.exitCode = 1; } }; switch (response.headers['content-encoding']) { case 'br': pipeline(response, zlib.createBrotliDecompress(), output, onError); break; // Or, just use zlib.createUnzip() to handle both of the following cases: case 'gzip': pipeline(response, zlib.createGunzip(), output, onError); break; case 'deflate': pipeline(response, zlib.createInflate(), output, onError); break; case 'zstd': pipeline(response, zlib.createZstdDecompress(), output, onError); break; default: pipeline(response, output, onError); break; } });// Client request example const zlib = require('node:zlib'); const http = require('node:http'); const fs = require('node:fs'); const { pipeline } = require('node:stream'); const request = http.get({ host: 'example.com', path: '/', port: 80, headers: { 'Accept-Encoding': 'br,gzip,deflate,zstd' } }); request.on('response', (response) => { const output = fs.createWriteStream('example.com_index.html'); const onError = (err) => { if (err) { console.error('An error occurred:', err); process.exitCode = 1; } }; switch (response.headers['content-encoding']) { case 'br': pipeline(response, zlib.createBrotliDecompress(), output, onError); break; // Or, just use zlib.createUnzip() to handle both of the following cases: case 'gzip': pipeline(response, zlib.createGunzip(), output, onError); break; case 'deflate': pipeline(response, zlib.createInflate(), output, onError); break; case 'zstd': pipeline(response, zlib.createZstdDecompress(), output, onError); break; default: pipeline(response, output, onError); break; } });
// server example // Running a gzip operation on every request is quite expensive. // It would be much more efficient to cache the compressed buffer. import zlib from 'node:zlib'; import http from 'node:http'; import fs from 'node:fs'; import { pipeline } from 'node:stream'; http.createServer((request, response) => { const raw = fs.createReadStream('index.html'); // Store both a compressed and an uncompressed version of the resource. response.setHeader('Vary', 'Accept-Encoding'); const acceptEncoding = request.headers['accept-encoding'] || ''; const onError = (err) => { if (err) { // If an error occurs, there's not much we can do because // the server has already sent the 200 response code and // some amount of data has already been sent to the client. // The best we can do is terminate the response immediately // and log the error. response.end(); console.error('An error occurred:', err); } }; // Note: This is not a conformant accept-encoding parser. // See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 if (/\bdeflate\b/.test(acceptEncoding)) { response.writeHead(200, { 'Content-Encoding': 'deflate' }); pipeline(raw, zlib.createDeflate(), response, onError); } else if (/\bgzip\b/.test(acceptEncoding)) { response.writeHead(200, { 'Content-Encoding': 'gzip' }); pipeline(raw, zlib.createGzip(), response, onError); } else if (/\bbr\b/.test(acceptEncoding)) { response.writeHead(200, { 'Content-Encoding': 'br' }); pipeline(raw, zlib.createBrotliCompress(), response, onError); } else if (/\bzstd\b/.test(acceptEncoding)) { response.writeHead(200, { 'Content-Encoding': 'zstd' }); pipeline(raw, zlib.createZstdCompress(), response, onError); } else { response.writeHead(200, {}); pipeline(raw, response, onError); } }).listen(1337);// server example // Running a gzip operation on every request is quite expensive. // It would be much more efficient to cache the compressed buffer. const zlib = require('node:zlib'); const http = require('node:http'); const fs = require('node:fs'); const { pipeline } = require('node:stream'); http.createServer((request, response) => { const raw = fs.createReadStream('index.html'); // Store both a compressed and an uncompressed version of the resource. response.setHeader('Vary', 'Accept-Encoding'); const acceptEncoding = request.headers['accept-encoding'] || ''; const onError = (err) => { if (err) { // If an error occurs, there's not much we can do because // the server has already sent the 200 response code and // some amount of data has already been sent to the client. // The best we can do is terminate the response immediately // and log the error. response.end(); console.error('An error occurred:', err); } }; // Note: This is not a conformant accept-encoding parser. // See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 if (/\bdeflate\b/.test(acceptEncoding)) { response.writeHead(200, { 'Content-Encoding': 'deflate' }); pipeline(raw, zlib.createDeflate(), response, onError); } else if (/\bgzip\b/.test(acceptEncoding)) { response.writeHead(200, { 'Content-Encoding': 'gzip' }); pipeline(raw, zlib.createGzip(), response, onError); } else if (/\bbr\b/.test(acceptEncoding)) { response.writeHead(200, { 'Content-Encoding': 'br' }); pipeline(raw, zlib.createBrotliCompress(), response, onError); } else if (/\bzstd\b/.test(acceptEncoding)) { response.writeHead(200, { 'Content-Encoding': 'zstd' }); pipeline(raw, zlib.createZstdCompress(), response, onError); } else { response.writeHead(200, {}); pipeline(raw, response, onError); } }).listen(1337);
By default, the zlib methods will throw an error when decompressing
truncated data. However, if it is known that the data is incomplete, or
the desire is to inspect only the beginning of a compressed file, it is
possible to suppress the default error handling by changing the flushing
method that is used to decompress the last chunk of input data:
// This is a truncated version of the buffer from the above examples
const buffer = Buffer.from('eJzT0yMA', 'base64');
zlib.unzip(
buffer,
// For Brotli, the equivalent is zlib.constants.BROTLI_OPERATION_FLUSH.
// For Zstd, the equivalent is zlib.constants.ZSTD_e_flush.
{ finishFlush: zlib.constants.Z_SYNC_FLUSH },
(err, buffer) => {
if (err) {
console.error('An error occurred:', err);
process.exitCode = 1;
}
console.log(buffer.toString());
});
This will not change the behavior in other error-throwing situations, e.g. when the input data has an invalid format. Using this method, it will not be possible to determine whether the input ended prematurely or lacks the integrity checks, making it necessary to manually check that the decompressed result is valid.
Memory usage tuning#
For zlib-based streams#
From zlib/zconf.h, modified for Node.js usage:
The memory requirements for deflate are (in bytes):
(1 << (windowBits + 2)) + (1 << (memLevel + 9));
That is: 128K for windowBits = 15 + 128K for memLevel = 8
(default values) plus a few kilobytes for small objects.
For example, to reduce the default memory requirements from 256K to 128K, the options should be set to:
const options = { windowBits: 14, memLevel: 7 };
This will, however, generally degrade compression.
The memory requirements for inflate are (in bytes) 1 << windowBits.
That is, 32K for windowBits = 15 (default value) plus a few kilobytes
for small objects.
This is in addition to a single internal output slab buffer of size
chunkSize, which defaults to 16K.
The speed of zlib compression is affected most dramatically by the
level setting. A higher level will result in better compression, but
will take longer to complete. A lower level will result in less
compression, but will be much faster.
In general, greater memory usage options will mean that Node.js has to make
fewer calls to zlib because it will be able to process more data on
each write operation. So, this is another factor that affects the
speed, at the cost of memory usage.
For Brotli-based streams#
There are equivalents to the zlib options for Brotli-based streams, although these options have different ranges than the zlib ones:
- zlib's
leveloption matches Brotli'sBROTLI_PARAM_QUALITYoption. - zlib's
windowBitsoption matches Brotli'sBROTLI_PARAM_LGWINoption.
See below for more details on Brotli-specific options.
For Zstd-based streams#
Stability: 1 - Experimental
There are equivalents to the zlib options for Zstd-based streams, although these options have different ranges than the zlib ones:
- zlib's
leveloption matches Zstd'sZSTD_c_compressionLeveloption. - zlib's
windowBitsoption matches Zstd'sZSTD_c_windowLogoption.
See below for more details on Zstd-specific options.
Flushing#
Calling .flush() on a compression stream will make zlib return as much
output as currently possible. This may come at the cost of degraded compression
quality, but can be useful when data needs to be available as soon as possible.
In the following example, flush() is used to write a compressed partial
HTTP response to the client:
import zlib from 'node:zlib'; import http from 'node:http'; import { pipeline } from 'node:stream'; http.createServer((request, response) => { // For the sake of simplicity, the Accept-Encoding checks are omitted. response.writeHead(200, { 'content-encoding': 'gzip' }); const output = zlib.createGzip(); let i; pipeline(output, response, (err) => { if (err) { // If an error occurs, there's not much we can do because // the server has already sent the 200 response code and // some amount of data has already been sent to the client. // The best we can do is terminate the response immediately // and log the error. clearInterval(i); response.end(); console.error('An error occurred:', err); } }); i = setInterval(() => { output.write(`The current time is ${Date()}\n`, () => { // The data has been passed to zlib, but the compression algorithm may // have decided to buffer the data for more efficient compression. // Calling .flush() will make the data available as soon as the client // is ready to receive it. output.flush(); }); }, 1000); }).listen(1337);const zlib = require('node:zlib'); const http = require('node:http'); const { pipeline } = require('node:stream'); http.createServer((request, response) => { // For the sake of simplicity, the Accept-Encoding checks are omitted. response.writeHead(200, { 'content-encoding': 'gzip' }); const output = zlib.createGzip(); let i; pipeline(output, response, (err) => { if (err) { // If an error occurs, there's not much we can do because // the server has already sent the 200 response code and // some amount of data has already been sent to the client. // The best we can do is terminate the response immediately // and log the error. clearInterval(i); response.end(); console.error('An error occurred:', err); } }); i = setInterval(() => { output.write(`The current time is ${Date()}\n`, () => { // The data has been passed to zlib, but the compression algorithm may // have decided to buffer the data for more efficient compression. // Calling .flush() will make the data available as soon as the client // is ready to receive it. output.flush(); }); }, 1000); }).listen(1337);