0
# Data Input/Output
1
2
Classes for reading from and writing to different data sources and destinations. These form the foundation for all ZIP operations in zip.js.
3
4
## Reader Classes
5
6
Readers provide data input for ZIP operations, supporting various data sources.
7
8
### Base Reader Class
9
10
```typescript { .api }
11
class Reader<Type> implements Initializable, ReadableReader {
12
constructor(value: Type);
13
readonly readable: ReadableStream;
14
readonly size: number;
15
init?(): Promise<void>;
16
readUint8Array(index: number, length: number): Promise<Uint8Array>;
17
}
18
```
19
20
All readers implement the same interface but read from different data sources.
21
22
### TextReader
23
24
Reads from string data.
25
26
```typescript { .api }
27
class TextReader extends Reader<string> {}
28
```
29
30
```javascript
31
import { TextReader } from "@zip.js/zip.js";
32
33
const reader = new TextReader("Hello, World!");
34
console.log(reader.size); // String length in bytes (UTF-8)
35
36
// Use with ZIP operations
37
const zipWriter = new ZipWriter(new BlobWriter());
38
await zipWriter.add("greeting.txt", reader);
39
```
40
41
### BlobReader
42
43
Reads from Blob objects (files, images, etc.).
44
45
```typescript { .api }
46
class BlobReader extends Reader<Blob> {}
47
```
48
49
```javascript
50
import { BlobReader } from "@zip.js/zip.js";
51
52
// From file input
53
const fileInput = document.querySelector('input[type="file"]');
54
const file = fileInput.files[0];
55
const reader = new BlobReader(file);
56
57
// From created blob
58
const textBlob = new Blob(["Some text content"], { type: "text/plain" });
59
const blobReader = new BlobReader(textBlob);
60
61
// Use in ZIP
62
await zipWriter.add(file.name, new BlobReader(file));
63
```
64
65
### Data64URIReader
66
67
Reads from Base64-encoded Data URIs.
68
69
```typescript { .api }
70
class Data64URIReader extends Reader<string> {}
71
```
72
73
```javascript
74
import { Data64URIReader } from "@zip.js/zip.js";
75
76
const dataUri = "data:text/plain;base64,SGVsbG8gV29ybGQ="; // "Hello World"
77
const reader = new Data64URIReader(dataUri);
78
79
await zipWriter.add("from-data-uri.txt", reader);
80
```
81
82
### Uint8ArrayReader
83
84
Reads from typed arrays (raw binary data).
85
86
```typescript { .api }
87
class Uint8ArrayReader extends Reader<Uint8Array> {}
88
```
89
90
```javascript
91
import { Uint8ArrayReader } from "@zip.js/zip.js";
92
93
// From binary data
94
const binaryData = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
95
const reader = new Uint8ArrayReader(binaryData);
96
97
// From ArrayBuffer
98
const buffer = new ArrayBuffer(1024);
99
const uint8Array = new Uint8Array(buffer);
100
const bufferReader = new Uint8ArrayReader(uint8Array);
101
102
await zipWriter.add("binary-data.bin", reader);
103
```
104
105
### HttpReader
106
107
Reads data from HTTP URLs.
108
109
```typescript { .api }
110
class HttpReader extends Reader<string> {
111
constructor(url: string | URL, options?: HttpOptions);
112
}
113
```
114
115
```javascript
116
import { HttpReader } from "@zip.js/zip.js";
117
118
// Simple HTTP read
119
const reader = new HttpReader("https://example.com/data.txt");
120
121
// With custom headers
122
const authenticatedReader = new HttpReader("https://api.example.com/file", {
123
headers: [
124
["Authorization", "Bearer your-token"],
125
["User-Agent", "MyApp/1.0"]
126
]
127
});
128
129
// Add remote file to ZIP without downloading to memory first
130
await zipWriter.add("remote-file.txt", reader);
131
```
132
133
### HttpRangeReader
134
135
Specialized HTTP reader that supports range requests for efficient partial downloads.
136
137
```typescript { .api }
138
class HttpRangeReader extends HttpReader {
139
constructor(url: string | URL, options?: HttpRangeOptions);
140
}
141
```
142
143
```javascript
144
import { HttpRangeReader } from "@zip.js/zip.js";
145
146
// Efficient for large remote files - only downloads needed parts
147
const reader = new HttpRangeReader("https://example.com/large-archive.zip", {
148
useRangeHeader: true,
149
preventHeadRequest: false
150
});
151
152
// Perfect for reading ZIP files via HTTP - only central directory is downloaded initially
153
const zipReader = new ZipReader(reader);
154
const entries = await zipReader.getEntries(); // Minimal download
155
```
156
157
### SplitDataReader
158
159
Reads from multiple data sources representing split archives.
160
161
```typescript { .api }
162
class SplitDataReader extends Reader<(Reader<unknown> | ReadableReader | ReadableStream)[]> {}
163
```
164
165
```javascript
166
import { SplitDataReader, BlobReader } from "@zip.js/zip.js";
167
168
// For split ZIP files (.z01, .z02, .zip)
169
const readers = [
170
new BlobReader(part1Blob), // .z01
171
new BlobReader(part2Blob), // .z02
172
new BlobReader(finalBlob) // .zip
173
];
174
175
const splitReader = new SplitDataReader(readers);
176
const zipReader = new ZipReader(splitReader);
177
```
178
179
## Writer Classes
180
181
Writers provide data output destinations for ZIP operations.
182
183
### Base Writer Class
184
185
```typescript { .api }
186
class Writer<Type> implements Initializable, WritableWriter {
187
readonly writable: WritableStream;
188
init?(size?: number): Promise<void>;
189
writeUint8Array(array: Uint8Array): Promise<void>;
190
getData(): Promise<Type>;
191
}
192
```
193
194
### TextWriter
195
196
Writes data as text with optional encoding.
197
198
```typescript { .api }
199
class TextWriter extends Writer<string> {
200
constructor(encoding?: string);
201
}
202
```
203
204
```javascript
205
import { TextWriter } from "@zip.js/zip.js";
206
207
// Default UTF-8 encoding
208
const writer = new TextWriter();
209
210
// Specific encoding
211
const latin1Writer = new TextWriter("latin1");
212
213
// Extract text from ZIP entry
214
const text = await fileEntry.getData(writer);
215
console.log(text); // Extracted text content
216
```
217
218
### BlobWriter
219
220
Writes data as a Blob with optional MIME type.
221
222
```typescript { .api }
223
class BlobWriter implements Initializable, WritableWriter {
224
constructor(mimeString?: string);
225
readonly writable: WritableStream;
226
init(): Promise<void>;
227
getData(): Promise<Blob>;
228
}
229
```
230
231
```javascript
232
import { BlobWriter } from "@zip.js/zip.js";
233
234
// General blob
235
const writer = new BlobWriter();
236
237
// Specific MIME type
238
const imageWriter = new BlobWriter("image/png");
239
const zipWriter = new BlobWriter("application/zip");
240
241
// Create ZIP file
242
const zipWriter = new ZipWriter(new BlobWriter("application/zip"));
243
await zipWriter.add("file.txt", new TextReader("content"));
244
const zipBlob = await zipWriter.close(); // Returns Blob
245
```
246
247
### Data64URIWriter
248
249
Writes data as a Base64-encoded Data URI.
250
251
```typescript { .api }
252
class Data64URIWriter extends Writer<string> {
253
constructor(mimeString?: string);
254
}
255
```
256
257
```javascript
258
import { Data64URIWriter } from "@zip.js/zip.js";
259
260
const writer = new Data64URIWriter("text/plain");
261
const dataUri = await fileEntry.getData(writer);
262
console.log(dataUri); // "data:text/plain;base64,SGVsbG8gV29ybGQ="
263
264
// Can be used directly in HTML
265
const img = document.createElement('img');
266
img.src = await imageEntry.getData(new Data64URIWriter("image/png"));
267
```
268
269
### Uint8ArrayWriter
270
271
Writes data as a typed array (raw binary).
272
273
```typescript { .api }
274
class Uint8ArrayWriter extends Writer<Uint8Array> {}
275
```
276
277
```javascript
278
import { Uint8ArrayWriter } from "@zip.js/zip.js";
279
280
const writer = new Uint8ArrayWriter();
281
const binaryData = await fileEntry.getData(writer);
282
283
console.log(`File size: ${binaryData.length} bytes`);
284
console.log(`First byte: 0x${binaryData[0].toString(16)}`);
285
```
286
287
### SplitDataWriter
288
289
Writes data to multiple destinations for creating split archives.
290
291
```typescript { .api }
292
class SplitDataWriter implements Initializable, WritableWriter {
293
constructor(
294
writerGenerator: AsyncGenerator<Writer<unknown> | WritableWriter | WritableStream, boolean>,
295
maxSize?: number
296
);
297
readonly writable: WritableStream;
298
init(): Promise<void>;
299
}
300
```
301
302
```javascript
303
import { SplitDataWriter, BlobWriter } from "@zip.js/zip.js";
304
305
// Create split ZIP files (1MB each)
306
async function* createSplitWriters() {
307
let partNumber = 1;
308
while (partNumber <= 10) { // Max 10 parts
309
const writer = new BlobWriter("application/zip");
310
writer.maxSize = 1024 * 1024; // 1MB chunks
311
console.log(`Creating part ${partNumber}`);
312
yield writer;
313
partNumber++;
314
}
315
return true; // Signal completion
316
}
317
318
const splitWriter = new SplitDataWriter(createSplitWriters(), 1024 * 1024);
319
const zipWriter = new ZipWriter(splitWriter);
320
321
// Add large files - they'll be automatically split
322
await zipWriter.add("large-file1.dat", new BlobReader(largeBlob1));
323
await zipWriter.add("large-file2.dat", new BlobReader(largeBlob2));
324
325
await zipWriter.close();
326
```
327
328
## HTTP Options
329
330
### HttpOptions Interface
331
332
```typescript { .api }
333
interface HttpOptions extends HttpRangeOptions {
334
useRangeHeader?: boolean;
335
forceRangeRequests?: boolean;
336
preventHeadRequest?: boolean;
337
combineSizeEocd?: boolean;
338
}
339
```
340
341
- **`useRangeHeader`**: Use HTTP Range headers for partial requests
342
- **`forceRangeRequests`**: Always use range requests even if not supported
343
- **`preventHeadRequest`**: Skip HEAD request to determine content length
344
- **`combineSizeEocd`**: Optimize by combining size detection with EOCD reading
345
346
### HttpRangeOptions Interface
347
348
```typescript { .api }
349
interface HttpRangeOptions {
350
useXHR?: boolean;
351
headers?: Iterable<[string, string]> | Map<string, string>;
352
}
353
```
354
355
- **`useXHR`**: Use XMLHttpRequest instead of fetch API
356
- **`headers`**: Custom HTTP headers
357
358
## Advanced Usage Examples
359
360
### Custom Data Sources
361
362
```javascript
363
// Create custom reader for database records
364
class DatabaseReader extends Reader {
365
constructor(query) {
366
super();
367
this.query = query;
368
this.data = null;
369
}
370
371
async init() {
372
// Fetch data from database
373
this.data = await database.query(this.query);
374
const jsonString = JSON.stringify(this.data);
375
this.size = new TextEncoder().encode(jsonString).length;
376
}
377
378
async readUint8Array(offset, length) {
379
if (!this.data) await this.init();
380
const jsonString = JSON.stringify(this.data);
381
const fullData = new TextEncoder().encode(jsonString);
382
return fullData.slice(offset, offset + length);
383
}
384
}
385
386
// Usage
387
const dbReader = new DatabaseReader("SELECT * FROM users");
388
await zipWriter.add("users.json", dbReader);
389
```
390
391
### Progressive Download with HttpRangeReader
392
393
```javascript
394
import { HttpRangeReader, ZipReader } from "@zip.js/zip.js";
395
396
async function analyzeRemoteZip(url) {
397
const reader = new HttpRangeReader(url, {
398
preventHeadRequest: false, // Get content length first
399
useRangeHeader: true // Use range requests
400
});
401
402
const zipReader = new ZipReader(reader);
403
404
// Only central directory is downloaded at this point
405
const entries = await zipReader.getEntries();
406
407
console.log(`ZIP contains ${entries.length} entries`);
408
409
// Download specific files on demand
410
const readmeEntry = entries.find(e => e.filename === 'README.md');
411
if (readmeEntry && !readmeEntry.directory) {
412
// Only this file's data is downloaded
413
const text = await readmeEntry.getData(new TextWriter());
414
console.log("README content:", text);
415
}
416
417
await zipReader.close();
418
}
419
```
420
421
### Memory-Efficient Large File Processing
422
423
```javascript
424
import { Uint8ArrayReader, Uint8ArrayWriter } from "@zip.js/zip.js";
425
426
class ChunkedFileProcessor {
427
constructor(file) {
428
this.file = file;
429
this.chunkSize = 1024 * 1024; // 1MB chunks
430
}
431
432
async processInChunks() {
433
const zipWriter = new ZipWriter(new BlobWriter("application/zip"));
434
435
for (let offset = 0; offset < this.file.size; offset += this.chunkSize) {
436
const chunk = this.file.slice(offset, offset + this.chunkSize);
437
const chunkData = new Uint8Array(await chunk.arrayBuffer());
438
439
// Process chunk (e.g., compress, encrypt, transform)
440
const processedChunk = this.processChunk(chunkData);
441
442
const chunkReader = new Uint8ArrayReader(processedChunk);
443
await zipWriter.add(`chunk-${Math.floor(offset / this.chunkSize)}.dat`, chunkReader);
444
}
445
446
return await zipWriter.close();
447
}
448
449
processChunk(data) {
450
// Example: simple XOR encryption
451
const key = 0xAA;
452
return data.map(byte => byte ^ key);
453
}
454
}
455
456
// Process large file without loading it entirely into memory
457
const processor = new ChunkedFileProcessor(largeFile);
458
const zipBlob = await processor.processInChunks();
459
```
460
461
### Custom Split Strategy
462
463
```javascript
464
import { SplitDataWriter, BlobWriter } from "@zip.js/zip.js";
465
466
// Create splits based on content type rather than size
467
async function* createContentBasedSplits() {
468
const splits = {
469
'application/json': new BlobWriter("application/zip"),
470
'text/plain': new BlobWriter("application/zip"),
471
'image/*': new BlobWriter("application/zip"),
472
'default': new BlobWriter("application/zip")
473
};
474
475
for (const [type, writer] of Object.entries(splits)) {
476
writer.contentType = type;
477
yield writer;
478
}
479
480
return true;
481
}
482
483
class ContentBasedSplitWriter extends SplitDataWriter {
484
constructor() {
485
super(createContentBasedSplits());
486
this.currentContentType = 'default';
487
}
488
489
setContentType(type) {
490
this.currentContentType = type;
491
}
492
}
493
494
// Usage would require custom ZIP writer integration
495
```
496
497
### Streaming Data Transformation
498
499
```javascript
500
// Transform data while reading/writing
501
class TransformReader extends Reader {
502
constructor(sourceReader, transformFn) {
503
super();
504
this.sourceReader = sourceReader;
505
this.transformFn = transformFn;
506
this.size = sourceReader.size; // May not be accurate after transform
507
}
508
509
async readUint8Array(offset, length) {
510
const sourceData = await this.sourceReader.readUint8Array(offset, length);
511
return this.transformFn(sourceData);
512
}
513
}
514
515
// Example: Uppercase text transform
516
function uppercaseTransform(data) {
517
const text = new TextDecoder().decode(data);
518
const uppercased = text.toUpperCase();
519
return new TextEncoder().encode(uppercased);
520
}
521
522
const originalReader = new TextReader("hello world");
523
const transformedReader = new TransformReader(originalReader, uppercaseTransform);
524
await zipWriter.add("uppercase.txt", transformedReader); // Contains "HELLO WORLD"
525
```
526
527
### Error Handling and Validation
528
529
```javascript
530
import {
531
ERR_HTTP_RANGE,
532
ERR_WRITER_NOT_INITIALIZED
533
} from "@zip.js/zip.js";
534
535
async function robustHttpRead(url, fallbackUrls = []) {
536
const allUrls = [url, ...fallbackUrls];
537
538
for (const currentUrl of allUrls) {
539
try {
540
const reader = new HttpRangeReader(currentUrl, {
541
useRangeHeader: true,
542
headers: [["User-Agent", "zip.js-client/1.0"]]
543
});
544
545
// Test if reader works by reading first few bytes
546
await reader.readUint8Array(0, 10);
547
return reader;
548
549
} catch (error) {
550
console.warn(`Failed to read from ${currentUrl}:`, error.message);
551
552
if (error.message === ERR_HTTP_RANGE) {
553
// Fall back to regular HttpReader
554
try {
555
return new HttpReader(currentUrl);
556
} catch (fallbackError) {
557
console.warn(`HTTP fallback also failed:`, fallbackError.message);
558
}
559
}
560
}
561
}
562
563
throw new Error("All URL sources failed");
564
}
565
566
// Usage
567
try {
568
const reader = await robustHttpRead(
569
"https://primary.example.com/file.zip",
570
[
571
"https://backup1.example.com/file.zip",
572
"https://backup2.example.com/file.zip"
573
]
574
);
575
576
const zipReader = new ZipReader(reader);
577
// Continue with ZIP processing
578
579
} catch (error) {
580
console.error("Could not establish reliable data source:", error);
581
}
582
```
583
584
The data I/O classes provide flexible and powerful ways to handle various data sources and destinations, forming the foundation for all ZIP operations in zip.js.