extract-zip vs adm-zip vs unzipper vs node-unzip-2
Handling ZIP File Extraction in Node.js Applications
extract-zipadm-zipunzippernode-unzip-2Similar Packages:
Handling ZIP File Extraction in Node.js Applications

adm-zip, extract-zip, node-unzip-2, and unzipper are all npm packages designed to work with ZIP archives in Node.js environments. They enable developers to extract, read, or manipulate ZIP files programmatically. While they share the common goal of ZIP file handling, they differ significantly in architecture, streaming support, memory usage, API design, and maintenance status. These differences directly impact their suitability for tasks ranging from simple one-off extractions to high-throughput server-side processing.

Npm Package Weekly Downloads Trend
3 Years
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
extract-zip17,983,331394-515 years agoBSD-2-Clause
adm-zip8,949,8642,143121 kB149a year agoMIT
unzipper5,674,85246856.6 kB85a year agoMIT
node-unzip-231,46644-217 years agoMIT

ZIP Extraction in Node.js: adm-zip vs extract-zip vs node-unzip-2 vs unzipper

Working with ZIP files in Node.js seems straightforward until you hit real-world constraints: large archives, memory limits, streaming requirements, or the need to modify archives on the fly. The four packages under review — adm-zip, extract-zip, node-unzip-2, and unzipper — each take a different approach to solving these problems. Let’s cut through the noise and compare them based on how they actually behave in practice.

⚠️ Deprecation Status: Don’t Use node-unzip-2

First, a critical warning: node-unzip-2 is deprecated. Its GitHub repository explicitly states it’s no longer maintained, and the npm page shows it hasn’t been updated in years. It relies on outdated stream implementations and has unresolved security issues. Do not use it in new projects. We include it here only because it still appears in legacy codebases, but unzipper was created as its spiritual successor and should be used instead.

🧠 Core Architecture: In-Memory vs Streaming

The biggest technical divide lies in how these libraries handle data flow.

adm-zip loads the entire ZIP file into memory before doing anything. This makes its API simple but dangerous for large files.

// adm-zip: Entire file loaded into RAM
const AdmZip = require('adm-zip');
const zip = new AdmZip('./archive.zip');
zip.extractAllTo('./output'); // Synchronous, blocks event loop

extract-zip uses yauzl under the hood, which parses ZIP files without loading everything into memory, but extract-zip itself doesn’t expose streaming — it just writes extracted files to disk.

// extract-zip: Extracts to disk, no streaming control
const extract = require('extract-zip');
await extract('./archive.zip', { dir: './output' });

unzipper is built for streaming from the ground up. It supports both event-based parsing and true Node.js streams, enabling memory-efficient processing.

// unzipper: Stream individual entries
const fs = require('fs');
const unzipper = require('unzipper');

fs.createReadStream('./archive.zip')
  .pipe(unzipper.Parse())
  .on('entry', (entry) => {
    const filePath = `./output/${entry.path}`;
    entry.pipe(fs.createWriteStream(filePath));
  });

🔌 API Style: Sync vs Async vs Streams

Your choice depends heavily on whether you can afford blocking operations.

adm-zip is mostly synchronous, which is convenient for scripts but disastrous in servers:

// adm-zip: Blocking I/O
const zip = new AdmZip('./large-file.zip');
zip.extractEntryTo('file.txt', './output'); // Freezes entire process

extract-zip provides a Promise-based async API, but only for full extraction:

// extract-zip: Async but all-or-nothing
await extract('./archive.zip', { dir: './output' });

unzipper gives you multiple async options: Promises for simple cases, streams for complex ones:

// unzipper: Promise-based extraction (like extract-zip)
await unzipper.Open.file('./archive.zip').then(d => d.extract({ path: './output' }));

// Or full streaming control
fs.createReadStream('./archive.zip')
  .pipe(unzipper.Extract({ path: './output' }));

💾 Memory Usage and Large File Support

If you’re dealing with ZIP files larger than a few hundred MB, memory becomes critical.

  • adm-zip: Will crash your process if the ZIP file exceeds available RAM. No workaround.
  • extract-zip: Handles large files reasonably well because yauzl reads incrementally, but you can’t process entries individually.
  • unzipper: Designed for gigabyte-sized archives. Streams decompress data on the fly without buffering entire entries.

Example of memory-safe large file handling with unzipper:

// Process huge ZIP without blowing memory
fs.createReadStream('huge-archive.zip')
  .pipe(unzipper.Parse())
  .on('entry', (entry) => {
    if (entry.path === 'target-file.txt') {
      entry.pipe(process.stdout); // Stream directly to output
    } else {
      entry.autodrain(); // Discard unwanted entries safely
    }
  });

Neither adm-zip nor extract-zip can do this efficiently.

🛠️ Feature Comparison: What Can You Actually Do?

Capabilityadm-zipextract-zipnode-unzip-2unzipper
Extract to disk
Extract to memory
Create/modify ZIPs✅ (limited)
Streaming extraction⚠️ (legacy)
Password-protected ZIPs
Async / non-blocking⚠️
Large file support⚠️

Creating ZIP files with unzipper:

// unzipper: Create ZIPs (basic support)
const { ZipFile } = require('unzipper');
const zip = new ZipFile();
zip.add('file.txt', 'Hello world');
const buffer = await zip.buffer();

adm-zip has more mature creation APIs:

// adm-zip: Full ZIP creation
const zip = new AdmZip();
zip.addFile('test.txt', Buffer.from('hello'));
zip.writeZip('new.zip'); // Synchronous write

🧪 Error Handling and Robustness

Error handling varies significantly:

  • adm-zip: Throws exceptions on invalid ZIPs; hard to recover from.
  • extract-zip: Rejects promises with clear errors; reliable for basic extraction.
  • unzipper: Emits 'error' events on streams and rejects promises appropriately. Handles malformed ZIPs gracefully.

Example with unzipper error handling:

const stream = fs.createReadStream('corrupt.zip')
  .pipe(unzipper.Parse())
  .on('error', (err) => {
    console.error('ZIP parsing failed:', err);
  });

adm-zip would crash the process on the same file.

🏗️ Real-World Recommendations

Scenario 1: Build Script Extracting Dependencies

You’re writing a CLI tool that downloads and extracts a ZIP of assets.

  • Best choice: extract-zip
  • Why? Simple, reliable, async, and you don’t need streaming or in-memory access.
// Clean and minimal
await extract(downloadPath, { dir: targetDir });

Scenario 2: Web Server Processing User-Uploaded ZIPs

Users upload ZIPs that your server must validate and extract selectively.

  • Best choice: unzipper
  • Why? Streaming prevents memory exhaustion, async keeps server responsive, and you can inspect entries before extraction.
// Handle uploads safely
req.pipe(unzipper.Parse())
  .on('entry', (entry) => {
    if (isSafePath(entry.path)) {
      entry.pipe(writeToStorage(entry.path));
    } else {
      entry.autodrain();
    }
  });

Scenario 3: Desktop App Modifying Game Mods (ZIP Files)

You need to add, remove, or update files inside existing ZIP archives.

  • Best choice: adm-zip
  • Why? Only it supports full archive mutation. Accept the memory trade-off since desktop apps have more RAM.
// Modify existing archive
const zip = new AdmZip('mod.zip');
zip.deleteFile('old-config.txt');
zip.addFile('new-config.txt', configBuffer);
zip.writeZip();

Scenario 4: Legacy Code Using node-unzip-2

You inherited code using node-unzip-2.

  • Action: Replace with unzipper immediately
  • Why? Security, compatibility, and maintenance risks are too high.

Migration example:

// Before (node-unzip-2)
fs.createReadStream('file.zip').pipe(unzip.Extract({ path: 'out' }));

// After (unzipper)
fs.createReadStream('file.zip').pipe(unzipper.Extract({ path: 'out' }));

📊 Summary Table

PackageBest ForAvoid WhenMemory SafetyAsync Ready
adm-zipZIP creation/modification, scriptsLarge files, servers
extract-zipSimple disk extractionNeed streaming or in-memory
node-unzip-2Never use in new projectsAlways⚠️⚠️
unzipperServers, large files, streamingNeed advanced ZIP creation

💡 Final Guidance

  • For 90% of modern applications, especially anything running on a server, unzipper is the right default choice. It’s safe, efficient, and flexible.
  • Only reach for adm-zip if you absolutely need to create or edit ZIP archives and can guarantee small file sizes.
  • Use extract-zip when you want the simplest possible “extract this ZIP to that folder” solution with zero configuration.
  • Never start a new project with node-unzip-2 — its time has passed.

The key is matching the tool to your actual constraints: memory limits, file size, need for streaming, and whether you’re reading or writing archives. Get that right, and ZIP handling stops being a headache.

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

    Choose extract-zip if your only requirement is extracting ZIP files to disk with minimal setup and you’re working in a standard Node.js environment. It’s a thin wrapper around yauzl that handles common extraction tasks reliably but offers no streaming, in-memory access, or archive modification capabilities. Ideal for CLI tools or build scripts where simplicity matters most.

  • adm-zip:

    Choose adm-zip if you need synchronous ZIP operations, full archive manipulation (create, update, delete entries), and don’t require streaming or large-file support. It loads entire archives into memory, making it unsuitable for large files or memory-constrained environments. Its synchronous nature also blocks the event loop, so avoid it in performance-sensitive server code.

  • unzipper:

    Choose unzipper if you need streaming extraction, low memory usage, support for large files, or integration with Node.js streams and async iterators. It supports both pull-based (event-driven) and push-based (streaming) APIs, handles password-protected archives, and works well in server environments. Use it when performance, scalability, or memory efficiency are priorities.

  • node-unzip-2:

    Do not choose node-unzip-2 for new projects. The package is deprecated and unmaintained, with known security vulnerabilities and compatibility issues with modern Node.js versions. While it once provided streaming extraction via legacy streams, its risks and lack of updates make it unsafe for production use. Evaluate unzipper as a direct replacement instead.

README for extract-zip

extract-zip

Unzip written in pure JavaScript. Extracts a zip into a directory. Available as a library or a command line program.

Uses the yauzl ZIP parser.

NPM Uses JS Standard Style Build Status

Installation

Make sure you have Node 10 or greater installed.

Get the library:

npm install extract-zip --save

Install the command line program:

npm install extract-zip -g

JS API

const extract = require('extract-zip')

async function main () {
  try {
    await extract(source, { dir: target })
    console.log('Extraction complete')
  } catch (err) {
    // handle any errors
  }
}

Options

  • dir (required) - the path to the directory where the extracted files are written
  • defaultDirMode - integer - Directory Mode (permissions), defaults to 0o755
  • defaultFileMode - integer - File Mode (permissions), defaults to 0o644
  • onEntry - function - if present, will be called with (entry, zipfile), entry is every entry from the zip file forwarded from the entry event from yauzl. zipfile is the yauzl instance

Default modes are only used if no permissions are set in the zip file.

CLI Usage

extract-zip foo.zip <targetDirectory>

If not specified, targetDirectory will default to process.cwd().