adm-zip、extract-zip、node-unzip-2、unzipperはすべてNode.js環境でZIPファイルを扱うためのnpmパッケージです。これらのライブラリは、ZIPアーカイブの展開(解凍)や作成といった基本的な機能を提供しますが、設計思想、APIスタイル、ストリーム対応、メモリ使用量、エラーハンドリングなどの点で大きく異なります。フロントエンド開発者がビルドツールやCI/CDパイプライン内でアセットのダウンロード・展開を行う際などに、適切なライブラリを選定する必要があります。
Node.jsでZIPファイルを扱う必要があるとき、どのライブラリを選ぶべきか迷うことはよくあります。adm-zip、extract-zip、node-unzip-2、unzipperはどれもZIPを扱いますが、内部実装や使い勝手、メモリ使用量、サポート範囲が大きく異なります。この記事では、実際のコードを交えながら、それぞれの強みと弱みを明らかにします。
extract-zip — 展開専用のミニマリストextract-zipは「ZIPをフォルダに展開する」ことだけに焦点を当てたライブラリです。APIは非常にシンプルで、Promiseを返す非同期関数一つで完結します。
// extract-zip
const extract = require('extract-zip');
await extract('archive.zip', { dir: './output' });
// archive.zip の中身が ./output ディレクトリに展開される
ZIP作成やファイルリスト取得はできませんが、その分軽量で信頼性が高いです。Electronのドキュメントでも紹介されているほど、標準的です。
adm-zip — 同期的で多機能adm-zipはZIPの読み込み・書き込み・編集を同期的に扱えるのが特徴です。ファイルリストの取得や個別ファイルの抽出も簡単です。
// adm-zip
const AdmZip = require('adm-zip');
const zip = new AdmZip('archive.zip');
zip.extractAllTo('./output'); // 全て展開
// 特定のファイルだけ取得
const entry = zip.getEntry('config.json');
if (entry) {
const data = entry.getData(); // Buffer
}
ただし、同期APIのため大容量ZIPではメインスレッドをブロックし、メモリに全データを読み込むため、1GB以上のZIPには不向きです。
node-unzip-2 — Streamベースの細かい制御node-unzip-2はStreamを使ってZIPを逐次処理します。メモリ使用量を抑えつつ、各ファイルのイベントをハンドリングできます。
// node-unzip-2
const fs = require('fs');
const unzip = require('node-unzip-2');
fs.createReadStream('archive.zip')
.pipe(unzip.Parse())
.on('entry', (entry) => {
const fileName = entry.path;
if (fileName === 'target.txt') {
entry.pipe(fs.createWriteStream(`./output/${fileName}`));
} else {
entry.autodrain(); // 処理しないファイルは破棄
}
});
ただし、このパッケージはオリジナルのnode-unzipのフォークであり、最近の更新が少ないため、新規プロジェクトでは慎重に検討すべきです。
unzipper — 現代的なStream & Promise対応unzipperはStreamとPromiseの両方をサポートし、柔軟な使い方が可能です。大容量ファイルにも対応し、API設計も洗練されています。
// unzipper — Promise版
const unzipper = require('unzipper');
await unzipper.Open.file('archive.zip')
.then(d => d.extract({ path: './output' }));
// unzipper — Stream版
fs.createReadStream('archive.zip')
.pipe(unzipper.Extract({ path: './output' }))
.on('close', () => console.log('完了'));
さらに、directory()メソッドを使えば、ZIP内のファイルを仮想ディレクトリのように扱えます。
const directory = await unzipper.Open.file('archive.zip');
const file = directory.files.find(f => f.path === 'data.json');
const content = await file.buffer(); // Bufferとして取得
extract-zip: 内部でyauzlを使用しており、Streamベースの展開を行うため、大容量ZIPでもメモリ効率が良いです。adm-zip: 全ZIPコンテンツをメモリに読み込むため、大きなファイルではプロセスがクラッシュする可能性があります。node-unzip-2: Stream処理なのでメモリ使用量は抑えられますが、エラーハンドリングがやや難しく、不安定なケースもあります。unzipper: Stream処理を前提として設計されており、大容量ファイルでも安定して動作します。また、Open.file()は必要な部分だけを読み込む遅延評価方式を採用しています。adm-zip: パスワード付きZIPの作成は可能ですが、展開はサポートしていません。extract-zip: パスワード付きZIPには対応していません。node-unzip-2: パスワード付きZIPの展開はサポートしていません。unzipper: file.buffer({ password: '...' }) のように、一部のメソッドでパスワード指定による展開が可能です(ただし、すべての暗号化方式に対応しているわけではありません)。// unzipper — パスワード付きZIP
const directory = await unzipper.Open.file('secure.zip');
const file = directory.files[0];
const data = await file.buffer({ password: 'secret' });
extract-zip: 安定しており、シンプルな用途には今でも十分使えます。adm-zip: 古くからあるライブラリで、バグフィックスはありますが、設計が古いため新規プロジェクトでは注意が必要です。node-unzip-2: オリジナルのnode-unzipは非推奨となっており、このフォークも最近の更新が少ないため、新規プロジェクトでは使用を避けることを強く推奨します。unzipper: アクティブにメンテナンスされており、Stream/Promiseの両対応、メモリ効率、拡張性のバランスが非常に良いです。extract-zipawait extract(pathToZip, { dir: targetDir });
unzipperconst dir = await unzipper.Open.file('bundle.zip');
const config = JSON.parse((await dir.files.find(f => f.path === 'config.json').buffer()).toString());
unzipper(Stream版)fs.createReadStream('logs.zip')
.pipe(unzipper.Parse())
.on('entry', async (entry) => {
if (entry.path.endsWith('.log')) {
const lines = [];
for await (const chunk of entry) {
lines.push(chunk.toString());
}
await saveToDB(lines);
} else {
entry.autodrain();
}
});
adm-zipconst zip = new AdmZip();
zip.addFile('report.txt', Buffer.from('Hello'));
const buffer = zip.toBuffer();
res.send(buffer);
| 機能 / ライブラリ | adm-zip | extract-zip | node-unzip-2 | unzipper |
|---|---|---|---|---|
| ZIP展開 | ✅(同期) | ✅(非同期) | ✅(Stream) | ✅(Stream/Promise) |
| ZIP作成 | ✅ | ❌ | ❌ | ✅(限定的) |
| 個別ファイルアクセス | ✅ | ❌ | ✅(Stream経由) | ✅(仮想ディレクトリ) |
| 大容量ファイル対応 | ❌(メモリ負荷高) | ✅ | ✅ | ✅ |
| パスワード付きZIP | 作成のみ | ❌ | ❌ | ✅(展開のみ) |
| 新規プロジェクト推奨 | △(小規模向け) | ✅(展開専用) | ❌(非推奨) | ✅(汎用) |
extract-zip — 単純で信頼性が高い。unzipper — 柔軟で安全。adm-zip — 手軽だが、サイズに注意。node-unzip-2 は避ける — 非推奨のフォークであり、代替手段が豊富にあるため。現代のNode.jsプロジェクトでは、unzipperが最もバランスの取れた選択肢です。Stream対応、Promise対応、メモリ効率、そしてアクティブなメンテナンス — これらすべてを兼ね備えているからです。一方で、用途が極めて限定的であれば、extract-zipのようなミニマルなライブラリも有効です。要件に合わせて、無駄のない選択を心がけましょう。
adm-zipはZIPファイルの読み書き両方に対応し、シンプルなAPIで同期的に操作できる点が特徴です。小規模なプロジェクトや、ZIP内のファイルリストを取得したり、特定のファイルだけを抽出したい場合に向いています。ただし、大容量ファイルではメモリ消費が大きくなる可能性があるため、注意が必要です。
extract-zipは「ZIPをディレクトリに展開する」ことに特化した軽量なライブラリです。非同期PromiseベースのAPIを提供し、ElectronやNext.jsなどの公式ドキュメントでも例として使われることがあります。ZIP作成機能はなく、読み取り専用ですが、用途が明確で安定しているため、単純な展開タスクには最適です。
node-unzip-2はオリジナルのnode-unzipのフォークで、Stream APIを活用してメモリ効率の良いZIP展開を実現します。大容量ZIPファイルを処理する必要があり、かつ細かい制御(例:ファイルごとのイベントハンドリング)が求められる場合に有効です。ただし、メンテナンス状況に注意が必要で、新規プロジェクトでは代替手段も検討すべきです。
unzipperは現代的なStream対応ZIPライブラリで、読み取り・展開だけでなく、ZIPの作成やパスワード保護ZIPのサポート(一部)も備えています。PromiseとStreamの両方のインターフェースを持ち、大規模ファイルにも対応可能です。バランスの取れた機能とアクティブなメンテナンスにより、多くのユースケースで推奨される選択肢です。
ADM-ZIP is a pure JavaScript implementation for zip data compression for NodeJS.
With npm do:
$ npm install adm-zip
Electron file system support described below.
The library allows you to:
There are no other nodeJS libraries that ADM-ZIP is dependent of
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.
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 });
.
.
.