0
# ZIP Writing
1
2
Creating and modifying ZIP files with support for compression, encryption, and various data sources.
3
4
## ZipWriter Class
5
6
The main class for creating ZIP files.
7
8
```typescript { .api }
9
class ZipWriter<Type> {
10
constructor(
11
writer: Writer<Type> | WritableWriter | WritableStream | AsyncGenerator<Writer<unknown> | WritableWriter | WritableStream, boolean>,
12
options?: ZipWriterConstructorOptions
13
);
14
15
readonly hasCorruptedEntries?: boolean;
16
17
prependZip<ReaderType>(
18
reader: Reader<ReaderType> | ReadableReader | ReadableStream | (Reader<unknown> | ReadableReader | ReadableStream)[]
19
): Promise<void>;
20
21
add<ReaderType>(
22
filename: string,
23
reader?: Reader<ReaderType> | ReadableReader | ReadableStream | (Reader<unknown> | ReadableReader | ReadableStream)[],
24
options?: ZipWriterAddDataOptions
25
): Promise<EntryMetaData>;
26
27
remove(entry: Entry | string): boolean;
28
close(comment?: Uint8Array, options?: ZipWriterCloseOptions): Promise<Type>;
29
}
30
```
31
32
### Constructor Parameters
33
34
- **`writer`**: Destination for the ZIP file. Can be a Writer instance, WritableStream, or generator for split archives
35
- **`options`**: Configuration options for writing behavior
36
37
### Properties
38
39
- **`hasCorruptedEntries`**: `true` if any entries were partially written (indicates corruption)
40
41
### Methods
42
43
#### add()
44
45
Adds a new entry to the ZIP file.
46
47
```javascript
48
// Add text file
49
await zipWriter.add("hello.txt", new TextReader("Hello World!"));
50
51
// Add binary file
52
await zipWriter.add("image.png", new BlobReader(imageBlob));
53
54
// Add directory
55
await zipWriter.add("folder/", null, { directory: true });
56
57
// Add with options
58
await zipWriter.add("secret.txt", new TextReader("Secret data"), {
59
password: "secret123",
60
level: 9,
61
lastModDate: new Date()
62
});
63
```
64
65
#### prependZip()
66
67
Adds an existing ZIP file at the beginning of the current ZIP. Must be called before any `add()` calls.
68
69
```javascript
70
const existingZipReader = new ZipReader(new BlobReader(existingZipBlob));
71
await zipWriter.prependZip(existingZipReader);
72
// Now add new entries
73
await zipWriter.add("new-file.txt", new TextReader("New content"));
74
```
75
76
#### remove()
77
78
Marks an entry for removal (entry won't be written to the final ZIP).
79
80
```javascript
81
const entryMetadata = await zipWriter.add("temp.txt", new TextReader("temp"));
82
const removed = zipWriter.remove(entryMetadata); // or remove("temp.txt")
83
console.log(removed); // true if entry was removed
84
```
85
86
#### close()
87
88
Finalizes the ZIP file by writing the central directory and closing the writer.
89
90
```javascript
91
const zipBlob = await zipWriter.close();
92
// Optional global comment
93
const zipBlobWithComment = await zipWriter.close(
94
new TextEncoder().encode("Created by my app")
95
);
96
```
97
98
## Writing Options
99
100
### ZipWriterConstructorOptions
101
102
```typescript { .api }
103
interface ZipWriterConstructorOptions {
104
zip64?: boolean;
105
preventClose?: boolean;
106
level?: number;
107
bufferedWrite?: boolean;
108
keepOrder?: boolean;
109
password?: string;
110
rawPassword?: Uint8Array;
111
encryptionStrength?: 1 | 2 | 3;
112
signal?: AbortSignal;
113
lastModDate?: Date;
114
lastAccessDate?: Date;
115
creationDate?: Date;
116
extendedTimestamp?: boolean;
117
zipCrypto?: boolean;
118
version?: number;
119
versionMadeBy?: number;
120
useUnicodeFileNames?: boolean;
121
dataDescriptor?: boolean;
122
dataDescriptorSignature?: boolean;
123
msDosCompatible?: boolean;
124
externalFileAttributes?: number;
125
internalFileAttributes?: number;
126
supportZip64SplitFile?: boolean;
127
usdz?: boolean;
128
passThrough?: boolean;
129
encrypted?: boolean;
130
offset?: number;
131
compressionMethod?: number;
132
encodeText?(text: string): Uint8Array | undefined;
133
}
134
```
135
136
#### Key Options
137
138
- **`level`**: Compression level 0-9 (0=no compression, 9=maximum compression, default: 6)
139
- **`password`**: Default password for encrypting entries
140
- **`encryptionStrength`**: AES encryption strength (1=128-bit, 2=192-bit, 3=256-bit, default: 3)
141
- **`zip64`**: Force ZIP64 format for large files (>4GB) or many entries (>65535)
142
- **`keepOrder`**: Maintain entry order in the file (improves web worker performance)
143
- **`bufferedWrite`**: Buffer entry data before writing (required for some scenarios)
144
- **`useUnicodeFileNames`**: Mark filenames as UTF-8 encoded
145
- **`lastModDate`**: Default last modification date for entries
146
- **`extendedTimestamp`**: Store extended timestamp information (access/creation dates)
147
- **`zipCrypto`**: Use legacy ZipCrypto encryption (not recommended, use AES instead)
148
- **`usdz`**: Create USDZ-compatible ZIP files
149
150
### ZipWriterAddDataOptions
151
152
```typescript { .api }
153
interface ZipWriterAddDataOptions extends ZipWriterConstructorOptions, EntryDataOnprogressOptions, WorkerConfiguration {
154
directory?: boolean;
155
executable?: boolean;
156
comment?: string;
157
extraField?: Map<number, Uint8Array>;
158
uncompressedSize?: number;
159
signature?: number;
160
}
161
```
162
163
Entry-specific options that override constructor defaults:
164
165
- **`directory`**: `true` to create a directory entry
166
- **`executable`**: `true` to mark file as executable
167
- **`comment`**: Entry-specific comment
168
- **`extraField`**: Custom extra field data
169
- **`uncompressedSize`**: Specify size if reader doesn't provide it
170
- **`signature`**: CRC32 signature if known in advance
171
172
### ZipWriterCloseOptions
173
174
```typescript { .api }
175
interface ZipWriterCloseOptions extends EntryOnprogressOptions {
176
zip64?: boolean;
177
preventClose?: boolean;
178
}
179
```
180
181
- **`zip64`**: Force ZIP64 format for central directory
182
- **`preventClose`**: Don't close the underlying writer stream
183
184
## Usage Examples
185
186
### Basic ZIP Creation
187
188
```javascript
189
import { ZipWriter, BlobWriter, TextReader, BlobReader } from "@zip.js/zip.js";
190
191
const zipWriter = new ZipWriter(new BlobWriter("application/zip"));
192
193
// Add text file
194
await zipWriter.add("readme.txt", new TextReader("Welcome to my app!"));
195
196
// Add binary file
197
await zipWriter.add("logo.png", new BlobReader(logoBlob));
198
199
// Add directory
200
await zipWriter.add("assets/", null, { directory: true });
201
await zipWriter.add("assets/style.css", new TextReader("body { margin: 0; }"));
202
203
// Finalize ZIP
204
const zipBlob = await zipWriter.close();
205
```
206
207
### Encrypted ZIP Creation
208
209
```javascript
210
const zipWriter = new ZipWriter(new BlobWriter("application/zip"), {
211
password: "secret123",
212
encryptionStrength: 3 // 256-bit AES
213
});
214
215
await zipWriter.add("confidential.txt", new TextReader("Top secret data"));
216
217
// Override password for specific entry
218
await zipWriter.add("public.txt", new TextReader("Public data"), {
219
password: undefined // No encryption for this entry
220
});
221
222
const encryptedZip = await zipWriter.close();
223
```
224
225
### High Compression ZIP
226
227
```javascript
228
const zipWriter = new ZipWriter(new BlobWriter("application/zip"), {
229
level: 9, // Maximum compression
230
keepOrder: true // Better performance with web workers
231
});
232
233
// Add large text files that compress well
234
await zipWriter.add("large-log.txt", new BlobReader(logFileBlob));
235
await zipWriter.add("data.json", new TextReader(JSON.stringify(largeDataObject)));
236
237
const compressedZip = await zipWriter.close();
238
```
239
240
### ZIP with Custom Metadata
241
242
```javascript
243
const zipWriter = new ZipWriter(new BlobWriter("application/zip"), {
244
extendedTimestamp: true,
245
useUnicodeFileNames: true
246
});
247
248
const customDate = new Date('2023-01-01T12:00:00Z');
249
250
await zipWriter.add("document.txt", new TextReader("Content"), {
251
lastModDate: customDate,
252
lastAccessDate: customDate,
253
creationDate: customDate,
254
comment: "Important document",
255
executable: false
256
});
257
258
const zipWithMetadata = await zipWriter.close(
259
new TextEncoder().encode("Archive created on " + new Date().toISOString())
260
);
261
```
262
263
### Split ZIP Creation
264
265
```javascript
266
// Create split ZIP files (useful for size limits)
267
async function* createSplitWriters() {
268
let partNumber = 1;
269
while (partNumber < 10) { // Max 10 parts
270
const writer = new BlobWriter("application/zip");
271
writer.maxSize = 1024 * 1024; // 1MB per part
272
yield writer;
273
partNumber++;
274
}
275
return true; // Signal completion
276
}
277
278
const zipWriter = new ZipWriter(createSplitWriters());
279
280
// Add files normally - they'll be split automatically
281
await zipWriter.add("large-file1.dat", new BlobReader(largeBlob1));
282
await zipWriter.add("large-file2.dat", new BlobReader(largeBlob2));
283
284
await zipWriter.close();
285
```
286
287
### Progress Tracking
288
289
```javascript
290
const zipWriter = new ZipWriter(new BlobWriter("application/zip"));
291
292
await zipWriter.add("large-file.bin", new BlobReader(largeBlob), {
293
onstart: (total) => {
294
console.log(`Starting compression of ${total} bytes`);
295
},
296
onprogress: (progress, total) => {
297
const percent = Math.round(progress / total * 100);
298
console.log(`Compression progress: ${percent}%`);
299
},
300
onend: (computedSize) => {
301
console.log(`Compression completed: ${computedSize} bytes processed`);
302
}
303
});
304
305
const zipBlob = await zipWriter.close({
306
onprogress: (progress, total, entry) => {
307
console.log(`Writing central directory: entry ${progress}/${total} (${entry.filename})`);
308
}
309
});
310
```
311
312
### Streaming from HTTP
313
314
```javascript
315
import { HttpReader } from "@zip.js/zip.js";
316
317
const zipWriter = new ZipWriter(new BlobWriter("application/zip"));
318
319
// Add file directly from URL without downloading to memory first
320
await zipWriter.add(
321
"remote-file.pdf",
322
new HttpReader("https://example.com/document.pdf")
323
);
324
325
await zipWriter.add(
326
"another-remote-file.jpg",
327
new HttpReader("https://example.com/image.jpg", {
328
headers: [["Authorization", "Bearer token123"]]
329
})
330
);
331
332
const zipBlob = await zipWriter.close();
333
```
334
335
### Modifying Existing ZIP
336
337
```javascript
338
// Read existing ZIP
339
const existingZipReader = new ZipReader(new BlobReader(existingZipBlob));
340
const existingEntries = await existingZipReader.getEntries();
341
342
// Create new ZIP with modifications
343
const zipWriter = new ZipWriter(new BlobWriter("application/zip"));
344
345
// Copy existing entries (except ones to modify)
346
for (const entry of existingEntries) {
347
if (entry.filename !== "file-to-replace.txt") {
348
await zipWriter.add(entry.filename,
349
entry.directory ? null : entry.getData(new Uint8ArrayReader(await entry.getData(new Uint8ArrayWriter())))
350
);
351
}
352
}
353
354
// Add new/replacement entries
355
await zipWriter.add("file-to-replace.txt", new TextReader("New content"));
356
await zipWriter.add("new-file.txt", new TextReader("Additional content"));
357
358
await existingZipReader.close();
359
const modifiedZip = await zipWriter.close();
360
```
361
362
### USDZ Creation (3D Assets)
363
364
```javascript
365
// Create USDZ file (ZIP-based format for 3D assets)
366
const zipWriter = new ZipWriter(new BlobWriter("model/vnd.usdz+zip"), {
367
usdz: true, // USDZ-specific formatting
368
level: 0, // USDZ typically uses no compression
369
useUnicodeFileNames: false
370
});
371
372
await zipWriter.add("scene.usdc", new BlobReader(sceneBlob));
373
await zipWriter.add("textures/diffuse.jpg", new BlobReader(textureBlob));
374
375
const usdzBlob = await zipWriter.close();
376
```
377
378
### Performance Optimization
379
380
```javascript
381
// Optimize for large ZIP creation
382
const zipWriter = new ZipWriter(new BlobWriter("application/zip"), {
383
level: 6, // Balanced compression
384
keepOrder: true, // Better web worker utilization
385
bufferedWrite: false, // Stream directly for better memory usage
386
useWebWorkers: true, // Enable parallel compression
387
useCompressionStream: true // Use native compression if available
388
});
389
390
// Add files in order of increasing size for better parallelization
391
const files = [smallFile1, smallFile2, mediumFile1, largeFile1];
392
for (const file of files) {
393
await zipWriter.add(file.name, new BlobReader(file.blob));
394
}
395
396
const optimizedZip = await zipWriter.close();
397
```
398
399
## Error Handling
400
401
```javascript
402
import {
403
ERR_DUPLICATED_NAME,
404
ERR_INVALID_ENTRY_NAME,
405
ERR_ZIP_NOT_EMPTY
406
} from "@zip.js/zip.js";
407
408
try {
409
const zipWriter = new ZipWriter(new BlobWriter("application/zip"));
410
411
await zipWriter.add("file.txt", new TextReader("content"));
412
413
// This will throw ERR_DUPLICATED_NAME
414
await zipWriter.add("file.txt", new TextReader("different content"));
415
416
} catch (error) {
417
if (error.message === ERR_DUPLICATED_NAME) {
418
console.error("Duplicate filename in ZIP");
419
} else if (error.message === ERR_INVALID_ENTRY_NAME) {
420
console.error("Invalid entry name");
421
} else {
422
console.error("Error creating ZIP:", error);
423
}
424
}
425
```
426
427
## Advanced Features
428
429
### Custom Compression
430
431
```javascript
432
// Use custom compression codec
433
import { configure } from "@zip.js/zip.js";
434
435
configure({
436
Deflate: CustomDeflateClass,
437
Inflate: CustomInflateClass
438
});
439
440
const zipWriter = new ZipWriter(new BlobWriter("application/zip"));
441
// ZIP will use custom compression
442
```
443
444
### Custom Worker Scripts
445
446
```javascript
447
configure({
448
workerScripts: {
449
deflate: ["./custom-deflate-worker.js"],
450
inflate: ["./custom-inflate-worker.js"]
451
}
452
});
453
```
454
455
### Memory Management
456
457
```javascript
458
// For very large ZIP files, use streaming and limit workers
459
configure({
460
maxWorkers: 2, // Limit concurrent workers
461
terminateWorkerTimeout: 1000 // Terminate workers quickly
462
});
463
464
const zipWriter = new ZipWriter(new BlobWriter("application/zip"), {
465
bufferedWrite: false, // Stream directly to reduce memory usage
466
keepOrder: false // Allow out-of-order writing for memory efficiency
467
});
468
```