adm-zip vs archiver vs node-zip vs yauzl vs yazl vs zip-lib
Node.js 环境下的 ZIP 文件处理方案深度对比与选型指南
adm-ziparchivernode-zipyauzlyazlzip-lib类似的npm包:

Node.js 环境下的 ZIP 文件处理方案深度对比与选型指南

adm-zip、archiver、node-zip、yauzl、yazl 和 zip-lib 都是 Node.js 生态中用于处理 ZIP 压缩文件的流行库,但它们的架构理念、性能特征和适用场景截然不同。adm-zip 倾向于将整个文件加载到内存中进行操作,适合处理小文件的快速读写;archiver 采用流式架构,专为处理大文件或实时数据传输设计;node-zip 是一个较旧的封装库,目前已不再推荐用于新项目;yauzl 和 yazl 分别提供了极致的流式解压和压缩能力,强调底层控制和内存效率;zip-lib 则试图在易用性和流式支持之间寻找平衡。理解这些库的核心差异对于构建高性能、低内存占用的后端服务或构建工具至关重要。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
adm-zip02,186151 kB612 个月前MIT
archiver02,97939.6 kB1756 个月前MIT
node-zip0217-1912 年前-
yauzl0831110 kB125 个月前MIT
yazl038958.7 kB222 年前MIT
zip-lib04568.8 kB15 个月前MIT

Node.js ZIP 库深度对决:内存、流式架构与工程选型

在 Node.js 后端开发中,处理 ZIP 文件是构建工具、数据导出服务和文件管理系统的常见需求。然而,不同的库在底层实现上有着本质的区别:有的将整个文件吞入内存,有的则像流水线一样逐字节处理。选错库可能导致服务器内存飙升甚至崩溃。本文将深入对比 adm-zip、archiver、node-zip、yauzl、yazl 和 zip-lib,帮助你做出正确的架构决策。

💾 核心架构:全量内存 vs 流式处理

这是选择 ZIP 库时最关键的分水岭。你的应用是需要处理几 KB 的配置文件,还是几百 MB 的数据备份?

adm-zip 采用全量内存模式。它在读取时会一次性将整个 ZIP 文件加载到 RAM 中,写入时也是如此。这使得 API 非常简单,可以直接随机访问文件列表,但代价是内存消耗与文件大小成正比。

// adm-zip: 整个文件被加载到内存
const AdmZip = require('adm-zip');
const zip = new AdmZip('./large-archive.zip');
// 此时整个 large-archive.zip 已进入内存
const entries = zip.getEntries(); 
zip.extractAllTo('./output', true);

archiver 采用流式架构。它不会等待所有文件准备就绪,而是边读取边压缩,数据像水流一样通过管道。这使得它可以处理远超可用内存大小的文件。

// archiver: 数据流式处理,内存占用恒定
const archiver = require('archiver');
const output = fs.createWriteStream('./archive.zip');
const archive = archiver('zip');

archive.pipe(output);
// 文件被分块读取并压缩,不会一次性加载
archive.file('./large-file.log', { name: 'log.txt' });
archive.finalize();

node-zip 同样倾向于全量处理,且基于较旧的接口设计。它通常需要将数据作为字符串或 Buffer 整体传入,缺乏现代流式处理的灵活性。

// node-zip: 整体数据处理
const zip = require('node-zip')();
zip.file('test.txt', 'hello');
// 生成整个 ZIP 的 Buffer
const data = zip.generate({ base64: false, compression: 'DEFLATE' });

yauzl 是纯粹的流式读取器。它专为解压设计,允许你逐个条目(entry)地打开流,极大降低了内存峰值。

// yauzl: 流式打开每个条目
const yauzl = require('yauzl');
yauzl.open('./large-archive.zip', { lazyEntries: true }, (err, zipfile) => {
  zipfile.on('entry', (entry) => {
    // 只有需要时才创建读取流
    zipfile.openReadStream(entry, (err, readStream) => {
      readStream.pipe(fs.createWriteStream(entry.fileName));
    });
  });
  zipfile.readEntry();
});

yazl 是纯粹的流式写入器。它不关心文件是否存在于磁盘,你可以通过流直接喂给它数据。

// yazl: 流式添加数据
const yazl = require('yazl');
const zipfile = new yazl.ZipFile();
zipfile.outputStream.pipe(fs.createWriteStream('output.zip'));

// 直接添加流,无需中间文件
zipfile.addReadStream(fs.createReadStream('source.txt'), 'dest.txt');
zipfile.end();

zip-lib 试图结合两者,提供基于 Promise 的流式支持。它既支持类似 adm-zip 的简单调用,也支持流式管道,但在极端大文件场景下的优化略逊于 archiver。

// zip-lib: 支持流式打包
const { pack } = require('zip-lib');
// 支持传入文件列表,内部处理流
await pack(['file1.txt', 'file2.txt'], 'output.zip');
// 也支持更底层的流操作

📂 文件添加方式:路径、流与动态内容

在实际工程中,我们往往需要压缩动态生成的内容(如数据库导出)而非仅仅是磁盘上的静态文件。

adm-zip 主要依赖文件系统路径。虽然它支持通过 Buffer 添加文件,但这意味着你需要先在内存中生成整个文件内容。

// adm-zip: 添加文件路径或 Buffer
const zip = new AdmZip();
zip.addLocalFile('./config.json'); // 从磁盘读取
zip.addFile('dynamic.txt', Buffer.from('Generated content')); // 需完整 Buffer

archiver 极其灵活,支持路径、流、Buffer 甚至子归档。它的 append 方法可以统一处理多种数据源。

// archiver: 统一接口处理多种源
const archive = archiver('zip');
archive.file('./static.txt', { name: 'static.txt' }); // 文件路径
archive.append(fs.createReadStream('./stream.txt'), { name: 'stream.txt' }); // 流
archive.append('string content', { name: 'text.txt' }); // 字符串

node-zip 接口较为单一,主要接受字符串或 Buffer。处理文件路径时需要配合 fs 模块手动读取,增加了样板代码。

// node-zip: 手动读取后添加
const content = fs.readFileSync('./file.txt');
zip.file('file.txt', content);

yauzl 仅负责解压,不涉及添加文件的逻辑。

yazl 专为流和 Buffer 设计。它没有“添加本地文件”的快捷方法,强制你显式地创建读取流,这反而保证了性能的可控性。

// yazl: 显式添加流或 Buffer
const zipfile = new yazl.ZipFile();
zipfile.addFile('./local.txt', 'remote.txt'); // 内部会创建流
zipfile.addBuffer(Buffer.from('data'), 'buffer.txt');
zipfile.addReadStream(myCustomStream, 'stream.txt');

zip-lib 提供了高层抽象,适合批量添加文件,但对于极度动态的流式数据,其 API 不如 archiver 直观。

// zip-lib: 批量添加
await pack(['file1', 'file2'], 'out.zip');
// 单个文件添加需使用 unzip/zip 对象实例

⚡ 解压能力与随机访问

如果你需要快速读取 ZIP 包中的某个特定文件而不解压全部内容,库的选择至关重要。

adm-zip 擅长随机访问。由于文件已在内存中,你可以瞬间获取任何条目的内容,非常适合读取小型配置包或资源包。

// adm-zip: 瞬间读取特定文件
const zip = new AdmZip('./archive.zip');
const content = zip.readAsText('config/settings.json');
// 无需遍历,直接访问

archiver 不支持解压。它是一个单向的压缩工具。如果需要解压,必须配合 unzipper 或 yauzl 使用。

// archiver: 无解压功能
// 必须引入其他库如 unzipper 来处理解压任务

node-zip 支持解压,但通常也是全量加载后提取,缺乏流式提取的精细控制。

// node-zip: 全量解析
zip.load(data);
const fileContent = zip.files['test.txt'].asText();

yauzl 是流式解压的王者。它支持 lazyEntries 模式,允许你在遍历过程中决定是否读取某个文件,甚至可以根据文件名跳过不需要的文件,极大节省 I/O。

// yauzl: 条件式流式解压
yauzl.open('./archive.zip', { lazyEntries: true }, (err, zipfile) => {
  zipfile.on('entry', (entry) => {
    if (entry.fileName.endsWith('.log')) {
      // 只解压日志文件
      zipfile.openReadStream(entry, (err, stream) => stream.pipe(...));
    } else {
      // 跳过其他文件,不消耗 I/O
      zipfile.readEntry();
    }
  });
  zipfile.readEntry();
});

yazl 不支持解压。它是专用的压缩库。

zip-lib 提供了解压功能,支持流式提取,API 比 yauzl 更现代化(基于 Promise),但在极端定制化解压逻辑上略逊一筹。

// zip-lib: Promise 风格的解压
const { unpack } = require('zip-lib');
await unpack('./archive.zip', './output-dir');
// 支持更细粒度的控制但文档相对较少

🛠️ 错误处理与稳定性

在 production 环境中,库的健壮性决定了系统的稳定性。

adm-zip 在处理损坏的 ZIP 文件时可能会抛出异常或行为不可预测。由于它是同步操作为主,错误处理需要使用 try-catch 块包裹,且在处理超大文件时容易触发 heap out of memory 错误。

// adm-zip: 需手动捕获同步错误
try {
  const zip = new AdmZip('./corrupted.zip');
} catch (e) {
  console.error('Failed to load zip', e);
}

archiver 拥有成熟的事件驱动错误处理。作为流,它通过 error 事件通知问题,能够优雅地处理管道中断或写入失败。

// archiver: 事件驱动的错误处理
archive.on('error', (err) => {
  throw err;
});
output.on('close', () => {
  console.log('Done');
});

node-zip 由于缺乏维护,对现代 Node.js 版本的兼容性较差,且在处理非标准 ZIP 头或特殊字符文件名时容易静默失败或崩溃。

yauzl 和 yazl 由同一作者维护,代码质量极高,严格遵循 ZIP 规范。它们对损坏文件的处理非常明确,会通过回调或事件报告具体错误,非常适合对数据完整性要求高的场景。

// yauzl: 明确的错误回调
yauzl.open('./bad.zip', (err) => {
  if (err) {
    // 明确知道是打开失败
    console.error('Open failed:', err);
  }
});

zip-lib 提供了基于 Promise 的错误处理,符合现代 async/await 习惯,但在面对极度畸形的 ZIP 文件时,其容错机制不如 yauzl 透明。

🏆 综合选型建议

特性adm-ziparchivernode-zipyauzlyazlzip-lib
核心模式内存全量流式管道内存全量流式读取流式写入混合/流式
内存占用高 (与文件大小成正比)低 (恒定)高极低极低低
压缩支持✅✅✅❌✅✅
解压支持✅❌✅✅❌✅
随机访问✅ 优秀❌⚠️ 一般⚠️ 需遍历❌⚠️ 一般
维护状态活跃活跃⚠️ 停止维护活跃活跃活跃
最佳场景小文件、脚本工具大文件、HTTP 流无 (遗留代码)专业解压工具动态内容压缩中等规模任务

💡 最终结论

  1. 拒绝 node-zip:如果你的项目中还在使用它,请制定计划迁移。它已不再是安全或可靠的选择。
  2. 大文件与生产服务首选 archiver:如果你正在构建一个文件下载 API、备份服务或需要处理用户上传的大型压缩包,archiver 是唯一的生产级选择。它的流式特性能保护你的服务器内存。
  3. 小文件与脚本工具选 adm-zip:对于 CLI 工具、单元测试中的 Mock 数据生成或处理几 MB 以内的配置文件,adm-zip 的同步 API 能极大简化代码逻辑。
  4. 极致性能与定制选 yauzl/yazl:如果你需要构建一个专业的解压软件,或者需要将动态数据流(如日志、数据库dump)直接压缩上传且对依赖体积敏感,这两个库提供了最纯净、高效的底层能力。
  5. 折中方案 zip-lib:如果你需要 Promise 风格的 API 且处理的文件规模中等,zip-lib 是一个现代化的替代方案,但在生态丰富度上略逊于 archiver。

在架构设计中,永远不要低估数据量增长带来的影响。今天能轻松放入内存的 50MB 文件,明天可能会变成 5GB 的日志归档。选择流式库(archiver, yauzl, yazl)通常是为未来扩展性留下的最佳保险。

如何选择: adm-zip vs archiver vs node-zip vs yauzl vs yazl vs zip-lib

  • adm-zip:

    如果你需要处理较小的文件(例如配置文件、小型构建产物)且追求 API 的简单直观,adm-zip 是一个不错的选择。它的同步 API 非常适合脚本任务或命令行工具,但由于它将整个 ZIP 文件加载到内存中,因此严禁用于处理大文件或高并发场景,以免导致内存溢出。

  • archiver:

    当你的应用场景涉及大文件处理、实时数据流(如直接管道传输到 HTTP 响应或云存储)或需要动态添加大量文件时,必须选择 archiver。它的流式架构确保了极低的内存占用,是构建文件下载服务、备份系统或大型构建流水线的行业标准。

  • node-zip:

    不建议在任何新项目中选择 node-zip。该库已长期缺乏维护,存在已知的设计局限性和潜在的安全风险,且其功能完全可以被更现代、更活跃的替代品(如 adm-zip 或 archiver)所覆盖。请将其视为遗留代码库中的存在,并在重构时优先替换。

  • yauzl:

    如果你需要极高的内存效率来解压大型 ZIP 文件,或者需要精细控制解压过程(例如只提取特定文件、处理加密条目或逐条读取元数据),yauzl 是最佳选择。它专注于“只读”场景,提供了比 adm-zip 更底层的流式访问能力,适合构建专业的解压工具或分析器。

  • yazl:

    当你需要一个轻量级、无依赖且完全基于流的 ZIP 创建工具时,应选择 yazl。它特别适合那些需要将动态生成的内容(如数据库查询结果、日志流)直接压缩并发送的场景。与 archiver 相比,它的 API 更底层,但换来了更小的体积和更少的抽象开销。

  • zip-lib:

    如果你希望获得比 adm-zip 更好的流式支持,同时又觉得 archiver 的插件系统过于复杂,zip-lib 提供了一个折中方案。它支持流式输入输出,API 设计相对现代,适合中等规模的文件处理任务,但在社区生态和高级功能(如复杂的加密支持)上可能不如 archiver 丰富。

adm-zip的README

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.