zlib.createZipArchive(entries[, options])
ZIP 压缩档案 API 是实验性的。第一次使用它的任何部分(包括这个函数)都会发出实验性警告;仅仅导入 node:zlib 不会。
🌐 The ZIP archive API is experimental. Using any part of it (this function among
them) emits an experimental warning the first time; merely importing
node:zlib does not.
entries<Iterable> | <AsyncIterable> 的ZipEntry.options<string> | <Object> 归档评论,作为{ comment: options }的简写。- 返回:<stream.Readable> 序列化归档的字节流。
将 entries 序列化到 ZIP 压缩包中,一旦条目数量或任何偏移量或大小超过经典 32/16 位 ZIP 字段能容纳的范围,就会自动切换到 Zip64 结构。返回的 Readable 也是同样 <Buffer> 块的 AsyncIterable 流式对象。
🌐 Serializes entries into a ZIP archive, switching to Zip64 structures
automatically once the entry count, or any offset or size, exceeds what the
classic 32-/16-bit ZIP fields can hold. The returned Readable is also an
AsyncIterable of the same <Buffer> chunks it streams.
条目是按迭代顺序写入的,而且不会对名称进行去重:如果一个可迭代对象生成两个同名条目,归档中会包含它们两个,而且大多数解压工具会保留后出现的那个。ZipBuffer 和 ZipFile 的 add() 方法则是按名称替换条目。
🌐 Entries are written in iteration order and nothing deduplicates names: an
iterable that yields two entries with the same name produces an archive
containing both, and most extraction tools keep the one that appears later.
ZipBuffer and ZipFile add() methods replace entries by name
instead.
返回的流拥有这些条目:每个条目在归档生成时会被使用,之后不能重复使用。这对于流式条目(来自 zlib.ZipEntry.createStream())尤其重要,因为它们持有底层资源,比如文件读取流。如果在完全消费之前销毁返回的流——例如 pipeline() 的目标失败——它会处理掉正在序列化的条目以及所有排队的条目,销毁它们的资源以防泄漏。要么将流消费到末尾,要么销毁它(直接销毁,通过失败的 pipeline(),或用 await using 销毁),以确保清理;一个既没有被消费也没有被销毁的流无法释放任何资源。一个从未交给归档的 ZipEntry 可以直接用 Symbol.dispose / Symbol.asyncDispose 释放。
🌐 The entries are owned by the returned stream: each is consumed as the archive
is produced and must not be reused afterwards. This matters for streaming
entries (from zlib.ZipEntry.createStream()), which hold an underlying
source such as a file read stream. If the returned stream is destroyed before
it is fully consumed - for example, the destination of a pipeline()
fails - it disposes the entry it was serializing and every entry still queued
behind it, destroying their sources so no descriptor leaks. Consume the stream
to the end, or destroy it (directly, through a failed pipeline(), or with
await using), to guarantee this cleanup; a stream that is neither consumed
nor destroyed cannot release anything. A ZipEntry that is never handed to
an archive can be released directly with Symbol.dispose / Symbol.asyncDispose.
如果归档注释经过 UTF-8 编码后超过 65,535 字节,会抛出 ERR_ZIP_ARCHIVE_TOO_LARGE 错误。
🌐 Throws an ERR_ZIP_ARCHIVE_TOO_LARGE error if the archive comment
exceeds 65,535 bytes when encoded as UTF-8.
import { createWriteStream } from 'node:fs';
import { pipeline } from 'node:stream/promises';
import { Buffer } from 'node:buffer';
import { ZipEntry, createZipArchive } from 'node:zlib';
const entries = [
await ZipEntry.create('hello.txt', Buffer.from('Hello, world!')),
await ZipEntry.create('data/', Buffer.alloc(0)),
];
await pipeline(
createZipArchive(entries, 'created by node:zlib'),
createWriteStream('archive.zip'),
);const { createWriteStream } = require('node:fs');
const { pipeline } = require('node:stream/promises');
const { ZipEntry, createZipArchive } = require('node:zlib');
async function main() {
const entries = [
await ZipEntry.create('hello.txt', Buffer.from('Hello, world!')),
await ZipEntry.create('data/', Buffer.alloc(0)),
];
await pipeline(
createZipArchive(entries, 'created by node:zlib'),
createWriteStream('archive.zip'),
);
}
main();传递 options.baseOffset 会生成一个归档文件,当它放在同一文件的其他内容之后时立即有效,而不依赖于读取器的自解压归档检测来补偿偏移:
🌐 Passing options.baseOffset produces an archive that is valid immediately
when placed after other content in the same file, without relying on a
reader's self-extracting-archive detection to compensate for the shift:
import { createWriteStream } from 'node:fs';
import { Buffer } from 'node:buffer';
import { ZipEntry, createZipArchive } from 'node:zlib';
const prefix = Buffer.from('#!/bin/sh\nexit 0\n');
const entries = [await ZipEntry.create('hello.txt', Buffer.from('Hello, world!'))];
const out = createWriteStream('self-extracting.zip');
out.write(prefix);
createZipArchive(entries, { baseOffset: prefix.byteLength }).pipe(out);