The ZIP archive API is experimental. Using any part of it (this class among
them) emits an experimental warning the first time; merely importing
node:zlib does not.
A random-access view over the entries of a ZIP archive on disk. Only the
archive's tail and central directory are read up front; member content is
read from disk lazily, on demand. Writable when opened with
{ writable: true }: zipFile.addEntry()/zipFile.add() append the
new member's data where the central directory used to be, then rewrite the
central directory immediately after it; zipFile.delete() just rewrites
the central directory. Both mean the file is altered as soon as the method's
returned Promise fulfills. Deleted or replaced members are left behind as
dead space; zipFile.compact() produces a stream with none.
These in-place edits are not crash-atomic. Rewriting the central directory
happens in place, so a write that fails partway - the disk fills, the device
disconnects, the process is killed - can leave the archive on disk with a
partial or missing central directory, i.e. unreadable, even though the member
data before it is intact. The rejected call surfaces the underlying error and
the ZipFile object is left usable (its in-memory view is not discarded, so a
caller can attempt recovery - for example re-writing the entries elsewhere with
zipFile.compact()), but that in-memory view may no longer match the bytes
on disk. Write to a copy, or compact() into a fresh file, when durability
across a failure matters.
Every method has a *Sync counterpart. As with the synchronous node:fs
APIs, these block the Node.js event loop and further JavaScript execution
until the operation completes; use them only where synchronous execution is
appropriate (for example, short-lived scripts or startup code), not in code
that must stay responsive. A synchronous method throws ERR_INVALID_STATE
if called while an asynchronous add(), addEntry(), delete(), or
close() on the same ZipFile has not settled yet, since letting the two
interleave could corrupt the archive.
import { ZipFile } from 'node:zlib'; import { Buffer } from 'node:buffer'; const zip = await ZipFile.open('archive.zip', { writable: true }); try { const entry = await zip.get('member.txt'); console.log((await entry.content()).toString()); for await (const chunk of await zip.stream('huge.bin')) { // Process each chunk without buffering the whole member. } await zip.add('new.txt', Buffer.from('hello')); await zip.delete('unwanted.txt'); } finally { await zip.close(); }
const { ZipFile } = require('node:zlib'); async function main() { const zip = await ZipFile.open('archive.zip', { writable: true }); try { const entry = await zip.get('member.txt'); console.log((await entry.content()).toString()); for await (const chunk of await zip.stream('huge.bin')) { // Process each chunk without buffering the whole member. } await zip.add('new.txt', Buffer.from('hello')); await zip.delete('unwanted.txt'); } finally { await zip.close(); } } main();
zlib.ZipFile.open(filename, options?): Promise
stringObjectboolean'r+'), enabling zipFile.addEntry()/zipFile.add()/
zipFile.delete(). Default: false.Throws an ERR_ZIP_ARCHIVE_TOO_LARGE error if the archive's central
directory is too large to buffer in memory.
zlib.ZipFile.openSync(filename, options?): ZipFile
The synchronous version of zlib.ZipFile.open().
zipFile.add(filename, data, options?): Promise
string/
marks a directory entry.Buffer | TypedArray | DataView | ArrayBufferObjectEquivalent to zipFile.addEntry(await zlib.ZipEntry.create(filename, data, options)).
zipFile.addEntry(entry): Promise
Writes entry where the central directory currently starts, then rewrites
the central directory to include it, replacing any existing entry of the
same name. Throws ERR_ZIP_NOT_WRITABLE if the ZipFile was not opened
with { writable: true }.
The returned (same) entry is left readable: a streaming entry created with
zlib.ZipEntry.createStream(), which would otherwise be spent once
serialized, is promoted in place to a file-backed entry pointing at the copy
just written (valid while this ZipFile is open). In-memory entries keep their
own buffer unchanged.
zipFile.addEntrySync(entry): ZipEntry
The synchronous version of zipFile.addEntry(). entry must not be a
pending streaming entry (one created with
zlib.ZipEntry.createStream()) - there is no synchronous way to drain
its asynchronous source.
zipFile.addSync(filename, data, options?): ZipEntry
string/
marks a directory entry.Buffer | TypedArray | DataView | ArrayBufferObjectZipEntryThe synchronous version of zipFile.add(). Equivalent to
zipFile.addEntrySync(zlib.ZipEntry.createSync(filename, data, options)).
zipFile.close(): Promise
PromiseCloses the underlying file handle.
Closing does not invalidate outstanding objects: ZipEntry objects previously
returned by zipFile.get() and the ZipFile's own methods will fail with
system-level errors (for example EBADF) if used after close, rather than a
dedicated Node.js error code. The same applies to zipFile.closeSync().
zipFile.closeSync(): void
The synchronous version of zipFile.close().
stringThe archive-level comment, preserved byte-for-byte across
zipFile.addEntry()/zipFile.delete() calls. The bytes are decoded
as UTF-8 when they are valid UTF-8 and as CP437 otherwise (the field carries
no encoding flag of its own).
zipFile.compact(comment?): stream.Readable
stringzipFile.comment.stream.ReadablezipFile.addEntry()/zipFile.delete() calls.Does not modify the open file; pipe the result into a new one:
import { createWriteStream } from 'node:fs'; zip.compact().pipe(createWriteStream('compacted.zip'));
zipFile.compactSync(comment?): Buffer
stringzipFile.comment.BufferzipFile.addEntry()/zipFile.delete() calls.The synchronous version of zipFile.compact(). Does not modify the
open file.
zipFile.delete(name): Promise
Rewrites the central directory without writing any new content - the
archive does not grow. Throws ERR_ZIP_NOT_WRITABLE if the ZipFile was
not opened with { writable: true }.
zipFile.deleteSync(name): boolean
The synchronous version of zipFile.delete().
zipFile.entries(): Iterator
zipFile.entriesSync(): Iterator
The synchronous version of zipFile.entries().
zipFile.forEach(callback, thisArg?): void
zipFile.forEachSync(callback, thisArg?): void
The synchronous version of zipFile.forEach(): callback is invoked
with a resolved ZipEntry instead of a Promise.
zipFile.get(name): Promise
Returns a lazy, file-backed ZipEntry for name. Nothing is read from
disk here and no content is buffered: the returned entry reads (and, for
zipEntry.content(), decompresses) its member straight from the file on
each access, and the ZipFile retains no member content. The entry is valid
only while this ZipFile is open. Reading its content later may throw
ERR_ZIP_ENTRY_TOO_LARGE if the member is too large to hold in a single
buffer; use zipEntry.contentIterator() (or zipFile.stream())
instead. Throws ERR_ZIP_ENTRY_NOT_FOUND if the archive has no entry
named name.
zipFile.getSync(name): ZipEntry
The synchronous version of zipFile.get(). Like get(), it reads
nothing up front and only builds the lazy handle, so it does not itself block
on I/O - but reads performed later through the returned entry (such as
zipEntry.contentSync()) do; see the note above on synchronous methods.
zipFile.has(name): boolean
zipFile.keys(): Iterator
IteratornumberThe number of entries in the archive.
zipFile.stream(name, options?): Promise
stringObjectPromisestream.Readable of the member's
decompressed content, without buffering the whole member in memory.Convenience wrapper that resolves to a Readable over
zipEntry.contentIterator() of zipFile.get()(name); the
compressed bytes are read from disk as the stream is consumed. The returned
promise rejects with ERR_ZIP_ENTRY_NOT_FOUND if the archive has no entry
named name.
zipFile.values(): Iterator
zipFile.valuesSync(): Iterator
The synchronous version of zipFile.values().
booleanWhether this ZipFile was opened with { writable: true }.