adm-zip vs extract-zip vs node-unzip-2 vs unzipper
Handling ZIP File Extraction in Node.js Applications
adm-zipextract-zipnode-unzip-2unzipperSimilar 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
adm-zip02,161122 kB144a month agoMIT
extract-zip0398-556 years agoBSD-2-Clause
node-unzip-2044-217 years agoMIT
unzipper047356.6 kB862 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: adm-zip vs extract-zip vs node-unzip-2 vs unzipper

  • 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.

  • 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.

  • 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.

  • 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.

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 });
.
.
.