adm-zip vs unzipper vs decompress-zip vs node-unzip-2
Extracting ZIP Archives in Node.js Applications
adm-zipunzipperdecompress-zipnode-unzip-2Similar Packages:
Extracting ZIP Archives in Node.js Applications

adm-zip, decompress-zip, node-unzip-2, and unzipper are all npm packages designed to handle ZIP archive extraction and manipulation in Node.js environments. These libraries enable developers to read, extract, create, or modify ZIP files programmatically — a common requirement in file processing pipelines, upload handlers, or data import/export features. While they share overlapping goals, their APIs, streaming capabilities, memory usage patterns, and maintenance status differ significantly, which directly impacts suitability for production systems.

Npm Package Weekly Downloads Trend
3 Years
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
adm-zip9,578,0142,143121 kB149a year agoMIT
unzipper5,986,19146856.6 kB86a year agoMIT
decompress-zip107,692102-295 years agoMIT
node-unzip-235,59044-217 years agoMIT

Extracting ZIP Archives in Node.js: adm-zip vs decompress-zip vs node-unzip-2 vs unzipper

Handling ZIP files in Node.js seems straightforward — until you hit real-world constraints like large file sizes, memory limits, or the need for streaming. The four packages under review each take a different approach to ZIP extraction, with significant implications for performance, reliability, and maintainability. Let’s cut through the noise.

⚠️ Deprecation and Maintenance Reality

Before diving into APIs, address the elephant in the room:

  • decompress-zip is officially deprecated on npm. Its GitHub repo is archived, and the last commit was in 2016. Do not use it in new code.
  • node-unzip-2 is a community fork of the abandoned node-unzip. Despite occasional npm publishes, its GitHub repository shows minimal activity since 2020 and unresolved critical issues (e.g., ZIP64 incompatibility). Treat it as unmaintained.
  • adm-zip and unzipper are actively maintained, with recent commits addressing security, compatibility, and feature requests.

🛑 Rule of thumb: Eliminate decompress-zip and node-unzip-2 from consideration for any new project. The remaining comparison is effectively between adm-zip and unzipper.

💾 Memory Model: In-Memory vs Streaming

How a library handles memory is often the deciding factor.

adm-zip: Full In-Memory Loading

adm-zip loads the entire ZIP file into memory upfront. This simplifies random access to entries but becomes problematic with large archives.

// adm-zip: Entire archive loaded into memory
const AdmZip = require('adm-zip');
const zip = new AdmZip('./archive.zip');

// List all entries
const entries = zip.getEntries();

// Extract a single file to buffer
const data = zip.readFile(entries[0]);

// Write modified archive back to disk
zip.writeZip('./modified.zip');

This works fine for archives under ~100MB, but can exhaust memory on larger files or under high concurrency.

unzipper: True Streaming

unzipper provides two streaming approaches:

  1. Push streams (Extract) for piping entries to disk or other streams.
  2. Pull streams (Open) for random access without full loading.
// unzipper: Streaming extraction to disk
const fs = require('fs');
const unzipper = require('unzipper');

fs.createReadStream('./archive.zip')
  .pipe(unzipper.Extract({ path: './output' }))
  .on('close', () => console.log('Done'));

// unzipper: Random access without full load
const directory = await unzipper.Open.file('./archive.zip');
const fileData = await directory.files[0].buffer(); // Only this file loaded

This keeps memory usage proportional to the largest single file in the archive — not the whole ZIP.

🔧 API Design: Convenience vs Control

Reading Entry Metadata

Getting file names, sizes, or modification times:

// adm-zip
const zip = new AdmZip('./archive.zip');
zip.getEntries().forEach(entry => {
  console.log(entry.entryName, entry.header.time);
});

// unzipper (pull model)
const dir = await unzipper.Open.file('./archive.zip');
dir.files.forEach(file => {
  console.log(file.path, file.lastModifiedDateTime);
});

Both work, but adm-zip gives immediate access after construction, while unzipper requires an async open step.

Extracting Specific Files

// adm-zip: Extract one file to buffer
const data = zip.readFile('config.json');

// unzipper: Extract one file to buffer
const file = dir.files.find(f => f.path === 'config.json');
const data = await file.buffer();

Again, adm-zip is synchronous once loaded; unzipper is promise-based.

Error Handling

unzipper propagates errors through standard Node.js stream events or promise rejections:

// unzipper: Stream error handling
fs.createReadStream('corrupt.zip')
  .pipe(unzipper.Extract({ path: './out' }))
  .on('error', err => console.error('Extraction failed:', err));

adm-zip throws exceptions synchronously:

// adm-zip: Try/catch required
try {
  const zip = new AdmZip('corrupt.zip');
} catch (err) {
  console.error('Invalid ZIP:', err);
}

📦 Creating and Modifying Archives

Only adm-zip supports creating or modifying ZIP files:

// adm-zip: Create new archive
const zip = new AdmZip();
zip.addFile('hello.txt', Buffer.from('Hello World'));
zip.writeZip('./new.zip');

// adm-zip: Modify existing
const zip = new AdmZip('./existing.zip');
zip.deleteFile('old.txt');
zip.addFile('new.txt', Buffer.from('Updated'));
zip.writeZip();

unzipper is extraction-only. If you need to generate ZIPs, adm-zip remains the only option among these four.

🌐 Real-World Scenarios

Scenario 1: User Uploads Large ZIP (1GB+)

  • Best choice: unzipper
  • Why? Streaming prevents memory exhaustion. Pipe directly from upload stream to extractor.
req.pipe(unzipper.Extract({ path: './uploads' }));

Scenario 2: Small Config Bundle (<10MB) with Need to Read Multiple Files

  • Best choice: adm-zip
  • Why? Simpler sync API, no async/await overhead, and memory impact is negligible.
const zip = new AdmZip(configBundle);
const manifest = JSON.parse(zip.readAsText('manifest.json'));
const schema = zip.readFile('schema.bin');

Scenario 3: Generate Reports as ZIP Archives

  • Best choice: adm-zip
  • Why? It’s the only one that supports archive creation.
const zip = new AdmZip();
zip.addFile('report.csv', csvBuffer);
zip.addFile('summary.txt', summaryBuffer);
return zip.toBuffer();

📊 Summary Table

Featureadm-zipdecompress-zipnode-unzip-2unzipper
Status✅ Maintained❌ Deprecated⚠️ Unmaintained✅ Maintained
Memory ModelFull in-memoryFull in-memoryStreamingStreaming
Create/Modify ZIPs✅ Yes❌ No❌ No❌ No
ZIP64 Support⚠️ Partial❌ No❌ No✅ Yes
Stream Extraction❌ No❌ No✅ Yes (legacy)✅ Yes (modern)
Error HandlingSync exceptionsUnreliableStream eventsStream events / Promises

💡 Final Recommendation

  • For extraction only: Use unzipper. It’s the only actively maintained, streaming-capable, ZIP64-compliant option.
  • For creating or modifying archives: Use adm-zip, but only with small files where memory isn’t a concern.
  • Never use: decompress-zip or node-unzip-2 — their risks far outweigh any perceived simplicity.

In modern Node.js applications — especially those handling user uploads or processing large datasets — streaming isn’t optional. That makes unzipper the default choice for extraction, while adm-zip fills a narrow but valid niche for archive generation and small-file manipulation.

How to Choose: adm-zip vs unzipper vs decompress-zip vs node-unzip-2
  • adm-zip:

    Choose adm-zip if you need synchronous ZIP operations with full in-memory access to entries (e.g., listing, reading, modifying, or writing archives without streams). It’s well-suited for small-to-medium archives where convenience outweighs memory concerns, but avoid it for large files or streaming scenarios due to its lack of stream support and potential memory pressure.

  • unzipper:

    Choose unzipper when you need robust, streaming-based ZIP extraction with low memory overhead, especially for large archives or server-side file processing. It supports both pull-streams (Open) and push-streams (Extract), handles ZIP64, and provides reliable error propagation. Ideal for scalable applications where memory efficiency and maintainability matter.

  • decompress-zip:

    Avoid decompress-zip in new projects — it is deprecated and unmaintained. The package hasn’t seen updates in years, lacks modern Node.js stream compatibility, and contains unresolved bugs around entry metadata and error handling. Use unzipper instead for similar event-driven extraction patterns.

  • node-unzip-2:

    Avoid node-unzip-2 for new development. Though it offers streaming extraction via Node.js streams, it is a fork of the long-abandoned node-unzip and shows no signs of active maintenance. Critical issues like incomplete ZIP64 support and poor error resilience make it risky for production use.

README for adm-zip

ADM-ZIP for NodeJS

ADM-ZIP is a pure JavaScript implementation for zip data compression for NodeJS.

Build Status

Installation

With npm do:

$ npm install adm-zip

Electron file system support described below.

What is it good for?

The library allows you to:

  • decompress zip files directly to disk or in memory buffers
  • compress files and store them to disk in .zip format or in compressed buffers
  • update content of/add new/delete files from an existing .zip

Dependencies

There are no other nodeJS libraries that ADM-ZIP is dependent of

Examples

Basic usage

var AdmZip = require("adm-zip");

// reading archives
var zip = new AdmZip("./my_file.zip");
var password = "1234567890";
var zipEntries = zip.getEntries(); // an array of ZipEntry records - add password parameter if entries are password protected

zipEntries.forEach(function (zipEntry) {
    console.log(zipEntry.toString()); // outputs zip entries information
    if (zipEntry.entryName == "my_file.txt") {
        console.log(zipEntry.getData().toString("utf8"));
    }
});
// outputs the content of some_folder/my_file.txt
console.log(zip.readAsText("some_folder/my_file.txt"));
// extracts the specified file to the specified location
zip.extractEntryTo(/*entry name*/ "some_folder/my_file.txt", /*target path*/ "/home/me/tempfolder", /*maintainEntryPath*/ false, /*overwrite*/ true);
// extracts everything
zip.extractAllTo(/*target path*/ "/home/me/zipcontent/", /*overwrite*/ true);

// creating archives
var zip = new AdmZip();

// add file directly
var content = "inner content of the file";
zip.addFile("test.txt", Buffer.from(content, "utf8"), "entry comment goes here");
// add local file
zip.addLocalFile("/home/me/some_picture.png");
// get everything as a buffer
var willSendthis = zip.toBuffer();
// or write everything to disk
zip.writeZip(/*target file name*/ "/home/me/files.zip");

// ... more examples in the wiki

For more detailed information please check out the wiki.

Electron original-fs

ADM-ZIP has supported electron original-fs for years without any user interractions but it causes problem with bundlers like rollup etc. For continuing support original-fs or any other custom file system module. There is possible specify your module by fs option in ADM-ZIP constructor.

Example:

const AdmZip = require("adm-zip");
const OriginalFs = require("original-fs");

// reading archives
const zip = new AdmZip("./my_file.zip", { fs: OriginalFs });
.
.
.