archiver, jszip, and pizzip are libraries used to create, read, and manipulate ZIP archives in JavaScript environments. archiver is a streaming interface designed primarily for Node.js server-side archive generation. jszip is a pure JavaScript library that works in both browsers and Node.js, focusing on in-memory manipulation of zip files. pizzip is a fork of jszip that aimed to address specific performance or compatibility issues, though its ecosystem presence is smaller. Choosing between them depends on whether you need server-side streaming, client-side manipulation, or specific legacy compatibility.
When working with ZIP archives in JavaScript, the choice of library dictates your architecture. archiver, jszip, and pizzip solve similar problems but target different environments and memory models. archiver focuses on Node.js streaming, while jszip and pizzip focus on in-memory manipulation across browsers and servers. Let's compare how they handle common engineering tasks.
archiver is built for Node.js.
stream modules.// archiver: Node.js only
const archiver = require('archiver');
const output = fs.createWriteStream('archive.zip');
const archive = archiver('zip');
archive.pipe(output);
archive.file('file1.txt', { name: 'file1.txt' });
archive.finalize();
jszip works everywhere.
// jszip: Universal
const JSZip = require('jszip');
const zip = new JSZip();
zip.file('file1.txt', 'Hello World');
zip.generateAsync({ type: 'blob' }).then(content => {
// Save in browser or Node
});
pizzip also works everywhere.
jszip so it inherits the universal runtime support.jszip.// pizzip: Universal
const PizZip = require('pizzip');
const zip = new PizZip();
zip.file('file1.txt', 'Hello World');
zip.generateAsync({ type: 'blob' }).then(content => {
// Save in browser or Node
});
archiver uses streams.
// archiver: Streaming input
const archive = archiver('zip');
// Stream a large file directly into the zip
archive.append(fs.createReadStream('large-video.mp4'), { name: 'video.mp4' });
archive.finalize();
jszip loads everything into memory.
// jszip: In-memory input
const zip = new JSZip();
// Must read file content first
const content = fs.readFileSync('large-video.mp4');
zip.file('video.mp4', content);
zip.generateAsync({ type: 'nodebuffer' });
pizzip also loads everything into memory.
jszip since it shares the core architecture.// pizzip: In-memory input
const zip = new PizZip();
const content = fs.readFileSync('large-video.mp4');
zip.file('video.mp4', content);
zip.generateAsync({ type: 'nodebuffer' });
archiver is for creation only.
unzipper for extraction.// archiver: No read support
// Cannot open an existing zip to inspect contents
// Must use a different package for extraction
jszip supports reading and writing.
// jszip: Read and modify
JSZip.loadAsync(existingZipBuffer).then(zip => {
zip.file('new.txt', 'New Content');
return zip.generateAsync({ type: 'blob' });
});
pizzip supports reading and writing.
jszip.// pizzip: Read and modify
PizZip.loadAsync(existingZipBuffer).then(zip => {
zip.file('new.txt', 'New Content');
return zip.generateAsync({ type: 'blob' });
});
archiver allows fine-tuned compression.
// archiver: Compression options
const archive = archiver('zip', {
zlib: { level: 9 } // Max compression
});
archive.file('file.txt', { name: 'file.txt' });
jszip supports compression levels.
// jszip: Compression options
zip.generateAsync({
type: 'blob',
compression: 'DEFLATE',
compressionOptions: { level: 9 }
});
pizzip supports compression levels.
jszip configuration API.// pizzip: Compression options
zip.generateAsync({
type: 'blob',
compression: 'DEFLATE',
compressionOptions: { level: 9 }
});
archiver is actively maintained.
jszip is actively maintained.
pizzip has limited activity.
jszip.jszip.While they target different layers, there is overlap in how they handle file data.
.file() method to add content.// All three support basic file addition
archive.file('name.txt', 'content'); // archiver
zip.file('name.txt', 'content'); // jszip
zip.file('name.txt', 'content'); // pizzip
// All produce standard .zip files
// Compatible with Windows Explorer, macOS Archive Utility, etc.
// jszip/pizzip example
zip.file('script.sh', content, { unixPermissions: '755' });
// archiver example
archive.file('script.sh', { mode: 0o755 });
| Feature | archiver | jszip | pizzip |
|---|---|---|---|
| Runtime | π₯οΈ Node.js Only | π Browser + Node | π Browser + Node |
| Memory | π Streaming (Low RAM) | π§ In-Memory (High RAM) | π§ In-Memory (High RAM) |
| Read/Write | βοΈ Write Only | π Read & Write | π Read & Write |
| Maintenance | β Active | β Active | β οΈ Limited |
| Best For | Server Backups, Large Exports | Client Downloads, Small Edits | Legacy JSZip Fork Needs |
archiver is the heavy lifter for servers ποΈ. Use it when you are on Node.js and need to zip large datasets without crashing your memory. It is the correct architectural choice for backend file processing.
jszip is the versatile tool for clients π οΈ. Use it when you need to zip files in the browser or handle small to medium archives in memory. It is the safest bet for universal JavaScript projects.
pizzip is a niche alternative π΅οΈ. Only consider it if you have a specific bug fix from its fork that jszip lacks and you can accept the maintenance risk. For most teams, the marginal gains do not justify the divergence from the main jszip ecosystem.
Final Thought: Match the tool to your runtime. If you are on the server streaming data, pick archiver. If you are in the browser or handling small files, pick jszip. Avoid pizzip unless you have a compelling, verified reason.
Choose archiver if you are building a Node.js backend service that needs to stream large files directly into a ZIP archive without loading everything into memory. It is the standard choice for server-side file compression where performance and memory efficiency are critical. Avoid it for browser-based projects as it relies on Node.js streams.
Choose jszip if you need to create or read ZIP files directly in the browser or need a simple in-memory solution for Node.js. It is ideal for scenarios where users upload multiple files that need to be bundled client-side before download. Be cautious with very large files since it loads the entire archive into memory.
Choose pizzip only if you have a specific requirement that jszip does not meet and you have verified its current maintenance status for your production needs. It is a fork of jszip and may offer niche fixes, but it carries higher risk due to a smaller community and potentially slower updates. For most new projects, jszip is the safer and more supported option.
A streaming interface for archive generation
Visit the API documentation for a list of all methods available.
npm install archiver --save
import fs from "fs";
import { ZipArchive } from "archiver";
// create a file to stream archive data to.
const output = fs.createWriteStream(__dirname + "/example.zip");
const archive = new ZipArchive({
zlib: { level: 9 }, // Sets the compression level.
});
// listen for all archive data to be written
// 'close' event is fired only when a file descriptor is involved
output.on("close", function () {
console.log(archive.pointer() + " total bytes");
console.log(
"archiver has been finalized and the output file descriptor has closed.",
);
});
// This event is fired when the data source is drained no matter what was the data source.
// It is not part of this library but rather from the NodeJS Stream API.
// @see: https://nodejs.org/api/stream.html#stream_event_end
output.on("end", function () {
console.log("Data has been drained");
});
// good practice to catch warnings (ie stat failures and other non-blocking errors)
archive.on("warning", function (err) {
if (err.code === "ENOENT") {
// log warning
} else {
// throw error
throw err;
}
});
// good practice to catch this error explicitly
archive.on("error", function (err) {
throw err;
});
// pipe archive data to the file
archive.pipe(output);
// append a file from stream
const file1 = __dirname + "/file1.txt";
archive.append(fs.createReadStream(file1), { name: "file1.txt" });
// append a file from string
archive.append("string cheese!", { name: "file2.txt" });
// append a file from buffer
const buffer3 = Buffer.from("buff it!");
archive.append(buffer3, { name: "file3.txt" });
// append a file
archive.file("file1.txt", { name: "file4.txt" });
// append files from a sub-directory and naming it `new-subdir` within the archive
archive.directory("subdir/", "new-subdir");
// append files from a sub-directory, putting its contents at the root of archive
archive.directory("subdir/", false);
// append files from a glob pattern
archive.glob("file*.txt", { cwd: __dirname });
// finalize the archive (ie we are done appending files but streams have to finish yet)
// 'close', 'end' or 'finish' may be fired right after calling this method so register to them beforehand
archive.finalize();
Archiver ships with out of the box support for TAR and ZIP archives.