adm-zip vs archiver vs node-zip vs yazl vs zip-a-folder vs zip-dir
Architectural Strategies for ZIP Compression in Node.js Environments
adm-ziparchivernode-zipyazlzip-a-folderzip-dirSimilar Packages:

Architectural Strategies for ZIP Compression in Node.js Environments

The selected packages represent two distinct approaches to handling ZIP archives in Node.js: stream-based processing and in-memory manipulation. Libraries like archiver and yazl focus on streaming data, allowing developers to zip large files or dynamic content without loading everything into RAM. This is critical for server-side applications handling large uploads or generating reports on the fly. Conversely, adm-zip and the deprecated node-zip operate by loading the entire archive into memory, offering random access to read or modify specific entries but struggling with large datasets. The remaining tools, zip-a-folder and zip-dir, are lightweight wrappers designed for specific, simple tasks like archiving a directory structure, often relying on the underlying engines of the more robust libraries.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
adm-zip02,182151 kB516 days agoMIT
archiver02,97739.6 kB1734 months agoMIT
node-zip0217-1911 years ago-
yazl038458.7 kB202 years agoMIT
zip-a-folder077147 kB024 days agoMIT
zip-dir047-206 years agoMIT

Architectural Strategies for ZIP Compression in Node.js Environments

When building backend services or build tools in Node.js, handling ZIP archives is a common requirement. Whether you are generating reports, bundling assets for deployment, or allowing users to download multiple files, the choice of library significantly impacts memory usage and performance. The ecosystem splits into two main categories: streaming libraries that process data piece-by-piece, and in-memory libraries that load the entire archive at once. Understanding this distinction is the key to making the right architectural decision.

🌊 Data Flow: Streaming vs. In-Memory

The most critical architectural decision is how data moves through your application. Streaming libraries allow you to pipe data directly from a source (like a file read stream or an HTTP request) into the ZIP creator. This means you can zip a 5GB file without ever holding 5GB in RAM. In-memory libraries read the whole file into a buffer, manipulate it, and then write it out. This is simpler to code but dangerous for large files.

archiver is the gold standard for streaming. It creates a stream that you can pipe to a file or an HTTP response. You add files one by one, and it processes them as data becomes available.

// archiver: Streaming large files safely
const archiver = require('archiver');
const output = fs.createWriteStream('archive.zip');
const archive = archiver('zip', { zlib: { level: 9 } });

output.on('close', () => console.log('Done'));
archive.pipe(output);

// Adds file to archive without loading full content into memory first
archive.file('large-video.mp4', { name: 'video.mp4' });
archive.finalize();

yazl (Yet Another Zip Library) is also a streaming library but with a much smaller footprint. It focuses purely on creating ZIP files via streams without the extra plugins or format support of archiver. It is excellent for microservices where dependency size matters.

// yazl: Lightweight streaming creation
const yazl = require('yazl');
const zipfile = new yazl.ZipFile();
const output = fs.createWriteStream('output.zip');

zipfile.outputStream.pipe(output);

// Add a file; yazl handles the stream internally
zipfile.addFile('path/to/file.txt', 'file.txt');
zipfile.end();

adm-zip operates entirely in memory. You load a file or create a new archive in RAM. This allows you to randomly access entries, delete specific files, or modify content before saving. However, if the ZIP file is larger than your available RAM, the process will crash.

// adm-zip: In-memory manipulation
const AdmZip = require('adm-zip');

// Loads entire archive into memory
const zip = new AdmZip('existing-archive.zip');

// Random access: Read a specific entry without extracting all
const entry = zip.getEntry('config.json');
const data = entry.getData(); // Returns a Buffer

// Modify and save back to disk (writes whole buffer again)
zip.deleteEntry('old-file.txt');
zip.writeZip('modified-archive.zip');

node-zip was an in-memory library similar to adm-zip. However, it is deprecated and should not be used. It lacks modern security patches and fails on newer Node.js versions. If you see this in a legacy codebase, plan to migrate immediately.

// node-zip: DEPRECATED - Do not use in new projects
// const zip = new require('node-zip')(); 
// This package is unmaintained and unsafe.

zip-a-folder and zip-dir are specialized wrappers. They typically use underlying libraries (often archiver or yazl internally) to handle the streaming but expose a simpler API specifically for zipping a local directory. They abstract the stream setup, making them easy for simple scripts but less flexible for custom data sources.

// zip-a-folder: Simple directory archiving
const zipAFolder = require('zip-a-folder');

// Wraps the complexity of traversing folders and streaming
await zipAFolder.zip('./my-project', './my-project.zip');
// zip-dir: Callback-based directory archiving
const zipdir = require('zip-dir');

zipdir('./src', { saveTo: './src-archive.zip' }, (err, buffer) => {
  // Returns a buffer (in-memory) after streaming the folder
  if (err) throw err;
  console.log('Archive created');
});

🛠️ Modifying Existing Archives

A common requirement is to update an existing ZIP file, such as adding a new log file to a previous day's archive. This is where the difference between streaming and in-memory becomes a hard constraint.

adm-zip excels here. Since it loads the central directory and all entries into memory, you can easily add, remove, or replace files before writing the result. It treats the ZIP file like a mutable object.

// adm-zip: Easy modification
const zip = new AdmZip('base.zip');
zip.addFile('new-log.txt', Buffer.from('Log data'));
zip.deleteEntry('temp.txt');
zip.writeZip('updated.zip');

archiver and yazl are primarily designed for creating new archives. They do not support modifying an existing ZIP file in place. To "update" an archive with these tools, you must create a new archive, pipe the contents of the old one into it, add your new files, and then replace the old file. This is more verbose but safer for very large files since you never hold two full copies in memory simultaneously if handled via streams.

// archiver: Simulating an update by merging streams
const archive = archiver('zip');
const output = fs.createWriteStream('new-archive.zip');

archive.pipe(output);

// Stream contents from old archive (requires extraction or reading entries)
// Note: archiver doesn't natively "open" a zip to append easily without re-adding files
archive.file('existing-file.txt', { name: 'existing-file.txt' }); 
archive.file('new-file.txt', { name: 'new-file.txt' });

archive.finalize();

zip-a-folder and zip-dir generally do not support modifying existing archives. They are built to take a source folder and produce a fresh ZIP file. Attempting to merge them with existing archives requires manual implementation outside their scope.

⚡ Performance and Memory Trade-offs

In a high-traffic API, memory leaks or spikes can crash your server. The choice of library directly affects this.

If you use adm-zip to zip a 500MB folder for a user download, your server's memory usage will jump by at least 500MB (often more due to buffer overhead) during the operation. If ten users request this simultaneously, you could easily exhaust server RAM.

// Risky in high-concurrency environments
async function downloadZip(req, res) {
  const zip = new AdmZip();
  zip.addLocalFolder('/huge-dataset'); // Loads ALL into RAM
  res.end(zip.toBuffer()); // Sends huge buffer
}

Using archiver or yazl solves this. The data flows through the server in chunks. The memory footprint remains constant regardless of the total archive size, allowing thousands of concurrent downloads without crashing.

// Safe for high concurrency
async function downloadZip(req, res) {
  const archive = archiver('zip');
  archive.pipe(res); // Pipes directly to HTTP response
  archive.directory('/huge-dataset', false);
  archive.finalize();
}

zip-dir returns a Buffer by default in its callback, which implies it collects the stream in memory before returning. This makes it less suitable for massive files compared to raw archiver streaming, though still better than adm-zip for the initial creation phase.

// zip-dir: Collects result in memory
zipdir('./large-folder', function (err, buffer) {
  // 'buffer' holds the entire ZIP in RAM here
  res.send(buffer);
});

📦 Feature Completeness and Ecosystem

Beyond basic zipping, real-world apps often need encryption, custom compression levels, or progress tracking.

archiver has a rich ecosystem. It supports multiple formats (ZIP, TAR, GZIP) via plugins. It allows you to listen to events like progress to send progress bars to clients, and it handles complex file permissions and symlinks robustly.

// archiver: Advanced features
archive.on('progress', (data) => {
  console.log(`Compressed ${data.entries.processed} of ${data.entries.total}`);
});

archive.append(fs.createWriteStream('log.txt'), { name: 'logs/app.log' });

yazl is intentionally minimal. It does one thing (create ZIPs) and does it well. It supports basic compression and encryption (via external crypto streams), but it lacks built-in progress events or multi-format support. You choose it for simplicity, not features.

adm-zip supports AES encryption and various compression methods, but because it is in-memory, applying heavy encryption on large files can be CPU and memory intensive. It is useful for desktop tools or CLI utilities where the user has dedicated resources, but less ideal for shared cloud servers.

zip-a-folder and zip-dir expose very few configuration options. They are "opinionated" tools that assume default compression and standard file handling. If you need to exclude certain file types (like .git folders) or change compression levels, you often have to fork the library or switch to archiver.

🏁 Final Recommendations

The landscape is clear once you separate use cases by scale and mutability.

  • For Server-Side APIs and Large Files: Always choose archiver. Its streaming nature protects your server from memory exhaustion, and its flexibility handles complex data sources (database blobs, HTTP streams, local files) effortlessly.
  • For Lightweight Microservices: If you only need to create simple ZIPs and want to minimize dependencies, yazl is an excellent, fast alternative to archiver.
  • For Desktop Tools or Small Configs: If you are building a CLI tool or a script that runs on a user's machine (where memory is less constrained) and you need to read/modify existing ZIPs, adm-zip provides the easiest API.
  • For Quick Scripts: If you just need to zip a folder in a build script and don't care about streaming or modification, zip-a-folder or zip-dir offer the shortest path to completion.
  • Avoid: Never start a new project with node-zip. It is obsolete.

By matching the library's data model (stream vs. memory) to your application's constraints, you ensure stability and performance in your Node.js architecture.

How to Choose: adm-zip vs archiver vs node-zip vs yazl vs zip-a-folder vs zip-dir

  • adm-zip:

    Choose adm-zip when you need to randomly access, read, or modify entries within an existing ZIP file entirely in memory. It is best suited for small-to-medium archives where the simplicity of synchronous API calls outweighs the memory cost. Avoid this package for large files or server environments with strict memory limits, as it loads the whole archive before processing.

  • archiver:

    Choose archiver for production-grade server applications that need to stream large files, combine multiple data sources, or apply compression on the fly. It is the ideal choice when memory efficiency is paramount, as it pipes data directly from sources to the output without buffering the entire archive. Its extensive plugin ecosystem also makes it suitable for complex archival formats beyond standard ZIP.

  • node-zip:

    Do NOT choose node-zip for any new project. This package is deprecated and unmaintained, posing security risks and compatibility issues with modern Node.js versions. Any functionality it once offered is superseded by safer, actively maintained alternatives like adm-zip for in-memory needs or archiver for streaming.

  • yazl:

    Choose yazl when you need a lightweight, dependency-free solution for creating ZIP files via streams. It is perfect for scenarios where you want to avoid the heavier footprint of archiver but still require streaming capabilities to handle dynamic content or large files. It excels in simplicity and speed for write-only operations where advanced features like encryption or complex appending are not required.

  • zip-a-folder:

    Choose zip-a-folder only for quick scripts or CLI tools where the sole requirement is to zip a single local directory recursively. It abstracts away the complexity of stream handling or file traversal, providing a simple Promise-based interface. Do not use it for complex archival logic, streaming from non-file sources, or fine-grained control over compression levels.

  • zip-dir:

    Choose zip-dir for straightforward tasks involving archiving a directory structure where a callback-based or Promise-based simple API is preferred. Like zip-a-folder, it is a wrapper that simplifies the process of traversing folders and adding them to an archive. It is suitable for build scripts or deployment tools but lacks the flexibility for streaming data from memory or network sources.

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

Security

Please report security vulnerabilities privately. See SECURITY.md.