adm-zip review
Opening a ZIP by filename with adm-zip reads the whole archive into a buffer. That's a useful trade for a bounded configuration bundle you need to inspect, change and save, but a costly one for an upload of unknown size. adm-zip 0.6.1 is a Node.js ZIP reader and writer with entry lookup, content replacement and extraction methods. Its synchronous API suits scripts and controlled background jobs. Choose it around a known working set, rather than assuming a filename means streamed input.
Use adm-zip 0.6.1 for bounded ZIP inspection and editing in Node jobs where synchronous reads and buffered output fit your budget. Before accepting user uploads, test resource limits and failure cleanup in your own service.
Answers from our run
adm-zip or jszip: which should you use?
jszip: Choose JSZip for creating, reading and editing ZIPs with JavaScript, especially when a browser Blob is the output you need. Its pinned README demonstrates generateAsync with type blob; this guide did not test browser compatibility. Use adm-zip 0.6.1 for bounded ZIP inspection and editing in Node jobs where synchronous reads and buffered output fit your budget.
When should you not use adm-zip?
You must keep archive input off the JavaScript thread. The filename constructor calls readFileSync, even if you later use an asynchronous method.
Use it if
- You need to replace a known file inside a ZIP and save a separate result, with enough memory for the archive and its contents.
- A local script must inspect entry names or read a small manifest without extracting everything to disk.
- Your build job collects a bounded set of local files and generated text, and synchronous filesystem calls are acceptable.
- You want Node.js ZIP reading and writing through one object, and can enforce limits outside the library.
- You must keep archive input off the JavaScript thread. The filename constructor calls readFileSync, even if you later use an asynchronous method.
- You need streamed output with backpressure. writeZip compresses to a complete buffer before writing, so this API shape is a poor match for that requirement.
- Uploads can expand beyond your disk or memory budget. Don't use these convenience extraction examples as an upload-security design.
- You need ZIP work in a browser. Evaluate JSZip for that environment instead of importing this Node filesystem API into a client bundle.
Setup reality
Install the version discussed here with npm install adm-zip@0.6.1, then load it in a CommonJS file with const AdmZip = require('adm-zip'). The published manifest declares Node >=14.0, points main to adm-zip.js and includes types.d.ts. That is a package declaration, not a recommendation to deploy an old Node release. No installation, benchmark, browser build or snippet execution was performed for this guide. The examples are illustrations checked against the version-matched implementation.
Budget for the input archive, decoded entry data and generated output. Opening a filename uses readFileSync, readFile returns entry data as a Buffer, and writeZip calls compressToBuffer before writing. An asynchronous write does not undo the constructor's synchronous read. There is no measured memory ceiling here. Keep these operations out of a latency-sensitive request handler unless your own workload checks justify them.
Treat edits as changes to the archive object until you serialize it. updateFile silently does nothing for a missing entry, so check getEntry before replacing content. readAsText returns an empty string when an entry is absent, which can hide a missing manifest. writeZip without a new path can overwrite the archive you opened. Pass an explicit result path and retain the original until you have validated the result.
Extraction has filesystem consequences. extractAllTo defaults overwrite and keepOriginalPermission to false. Use a fresh destination and handle errors, since an exception can leave files already written. For untrusted input, enforce upload limits, resource isolation and a cleanup policy outside these snippets. The 0.6.1 release notes report fixes for malformed DEFLATE data, symlink handling, duplicate entry names and decompression size-cap enforcement. Those fixes don't establish a security guarantee for your service.
Scores below are editorial source assessments on a 1-5 scale: 1 means poor fit or thin evidence for that dimension, 3 means useful evidence with material limits, and 5 requires unusually complete support. API stability considers the declared version and method contracts. Documentation quality considers how clearly those contracts expose pitfalls. Maintenance considers the release's recorded fixes, while ecosystem considers documented integration paths. They are not test scores or adoption measurements.
Patterns
Build a ZIP from generated text create-buffer
const AdmZip = require('adm-zip')
const zip = new AdmZip()
zip.addFile('config.json', Buffer.from('{"enabled":true}\n'))
const result = zip.toBuffer()
require('node:fs').writeFileSync('bundle.zip', result)Source-checked illustration, not executed. toBuffer returns the entire ZIP as a Buffer; the final filesystem write is synchronous too.
Inspect a bounded archive list-entry-names
const AdmZip = require('adm-zip')
const zip = new AdmZip('bundle.zip')
for (const entry of zip.getEntries()) {
console.log(entry.entryName)
}getEntries returns entry objects, including directories. Opening the filename has already read the archive synchronously; listing names is not streamed input.
Check that a manifest exists before decoding read-manifest
const AdmZip = require('adm-zip')
const zip = new AdmZip('bundle.zip')
const entry = zip.getEntry('config.json')
if (!entry || entry.isDirectory) throw new Error('Missing config file')
const config = JSON.parse(zip.readAsText(entry, 'utf8'))
console.log(config)readAsText returns an empty string for an absent entry. The explicit lookup keeps that case separate from invalid or empty JSON; parsing can still throw.
Keep binary content as a Buffer read-binary-entry
const AdmZip = require('adm-zip')
const zip = new AdmZip('bundle.zip')
const entry = zip.getEntry('assets/icon.png')
if (!entry || entry.isDirectory) throw new Error('Missing icon file')
const bytes = zip.readFile(entry)
if (bytes === null) throw new Error('No icon data')
console.log(bytes.length)readFile returns entry data as a Buffer or null when lookup fails. Corrupt data can throw; the null check is not comprehensive archive validation.
Replace an existing config and save a copy replace-entry
const AdmZip = require('adm-zip')
const zip = new AdmZip('bundle.zip')
const entry = zip.getEntry('config.json')
if (!entry || entry.isDirectory) throw new Error('Missing config file')
zip.updateFile(entry, Buffer.from('{"enabled":false}\n'))
zip.writeZip('bundle-edited.zip')updateFile silently ignores a missing entry. The lookup prevents a false-success update, and an explicit output path avoids writeZip falling back to the original filename. An existing result file can still be overwritten.
Remove a file before serializing remove-entry
const AdmZip = require('adm-zip')
const zip = new AdmZip('bundle.zip')
const entry = zip.getEntry('notes.txt')
if (!entry || entry.isDirectory) throw new Error('Missing notes file')
zip.deleteFile(entry)
zip.writeZip('bundle-without-notes.zip')deleteFile changes the archive object. You still need to serialize it. This example rejects directories because deleting a directory can also remove its nested entries.
Map a local file to an archive path add-local-file
const AdmZip = require('adm-zip')
const zip = new AdmZip()
zip.addLocalFile('config.json', 'settings', 'app.json')
zip.writeZip('settings.zip')The arguments are local path, archive directory and optional archive filename. The entry becomes settings/app.json. addLocalFile uses synchronous stat and file reads, and throws if the source is absent.
Extract a trusted bundle without overwriting files extract-to-fresh-directory
const AdmZip = require('adm-zip')
const fs = require('node:fs')
const os = require('node:os')
const path = require('node:path')
const target = fs.mkdtempSync(path.join(os.tmpdir(), 'bundle-'))
try {
new AdmZip('bundle.zip').extractAllTo(target, false, false)
console.log(target)
} catch (error) {
fs.rmSync(target, { recursive: true, force: true })
throw error
}The booleans disable overwrite and restoration of archived permissions. A fresh directory reduces collisions, but does not provide resource isolation for hostile archives. This illustration removes partial output on failure; the caller owns cleanup after success.
Open an archive already held in memory load-known-buffer
const AdmZip = require('adm-zip')
const fs = require('node:fs')
const input = fs.readFileSync('bundle.zip')
const zip = new AdmZip(input)
console.log(Boolean(zip.getEntry('config.json')))Pass a Node Buffer, rather than a stream. Reading the file into a Buffer here makes the memory boundary explicit; it doesn't save memory compared with opening the filename. All examples in this guide remain unexecuted illustrations.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| jszip | npm | Choose JSZip for creating, reading and editing ZIPs with JavaScript, especially when a browser Blob is the output you need. Its pinned README demonstrates generateAsync with type blob; this guide did not test browser compatibility. |
| yauzl | npm | Choose yauzl to read ZIP entries through asynchronous APIs when you want to avoid buffering entire files. Its documented focus is unzipping; choose a separate writer if you also need to create archives. |
More ai / ml guides
@types/node · openai · @modelcontextprotocol/sdk · mcp · huggingface-hub · @anthropic-ai/sdk · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.