0
# Filesystem API
1
2
High-level filesystem-like operations for complex ZIP manipulation, providing an intuitive interface for managing ZIP archives as virtual filesystems.
3
4
## fs Object
5
6
The main entry point for the filesystem API.
7
8
```typescript { .api }
9
const fs: {
10
FS: typeof FS;
11
ZipDirectoryEntry: typeof ZipDirectoryEntry;
12
ZipFileEntry: typeof ZipFileEntry;
13
};
14
```
15
16
### Usage
17
18
```javascript
19
import { fs } from "@zip.js/zip.js";
20
21
// Create a new filesystem
22
const zipFs = new fs.FS();
23
24
// Add files and directories
25
zipFs.addText("readme.txt", "Welcome!");
26
const assetsDir = zipFs.addDirectory("assets");
27
assetsDir.addBlob("logo.png", logoBlob);
28
29
// Export as ZIP
30
const zipBlob = await zipFs.exportBlob();
31
```
32
33
## FS Class
34
35
The root filesystem class that manages ZIP entries as a virtual filesystem.
36
37
```typescript { .api }
38
class FS extends ZipDirectoryEntry {
39
readonly root: ZipDirectoryEntry;
40
41
remove(entry: ZipEntry): void;
42
move(entry: ZipEntry, destination: ZipDirectoryEntry): void;
43
find(fullname: string): ZipEntry | undefined;
44
getById(id: number): ZipEntry | undefined;
45
}
46
```
47
48
### Properties
49
50
- **`root`**: The root directory entry
51
52
### Methods
53
54
#### remove()
55
56
Removes an entry and all its children from the filesystem.
57
58
```javascript
59
const fileEntry = zipFs.addText("temp.txt", "temporary");
60
zipFs.remove(fileEntry); // File is removed from filesystem
61
```
62
63
#### move()
64
65
Moves an entry to a different directory.
66
67
```javascript
68
const file = zipFs.addText("misplaced.txt", "content");
69
const targetDir = zipFs.addDirectory("correct-location");
70
zipFs.move(file, targetDir); // Now at "correct-location/misplaced.txt"
71
```
72
73
#### find()
74
75
Finds an entry by its full path.
76
77
```javascript
78
const entry = zipFs.find("assets/images/logo.png");
79
if (entry && !entry.directory) {
80
const blob = await entry.getBlob();
81
}
82
```
83
84
#### getById()
85
86
Finds an entry by its unique ID.
87
88
```javascript
89
const entry = zipFs.getById(123);
90
if (entry) {
91
console.log(`Found: ${entry.getFullname()}`);
92
}
93
```
94
95
## ZipEntry Base Class
96
97
Base class for all filesystem entries.
98
99
```typescript { .api }
100
class ZipEntry {
101
name: string;
102
data?: EntryMetaData;
103
id: number;
104
parent?: ZipEntry;
105
uncompressedSize: number;
106
children: ZipEntry[];
107
108
clone(deepClone?: boolean): ZipEntry;
109
getFullname(): string;
110
getRelativeName(ancestor: ZipDirectoryEntry): string;
111
isDescendantOf(ancestor: ZipDirectoryEntry): boolean;
112
isPasswordProtected(): boolean;
113
checkPassword(password: string, options?: EntryGetDataOptions): Promise<boolean>;
114
rename(name: string): void;
115
}
116
```
117
118
### Properties
119
120
- **`name`**: Entry name (without path)
121
- **`data`**: Associated metadata from ZIP file
122
- **`id`**: Unique identifier
123
- **`parent`**: Parent directory
124
- **`uncompressedSize`**: Size of uncompressed content
125
- **`children`**: Child entries (for directories)
126
127
### Methods
128
129
#### getFullname()
130
131
Returns the complete path of the entry.
132
133
```javascript
134
const file = assetsDir.addText("style.css", "body {}");
135
console.log(file.getFullname()); // "assets/style.css"
136
```
137
138
#### getRelativeName()
139
140
Returns the path relative to an ancestor directory.
141
142
```javascript
143
const deepFile = zipFs.find("assets/images/icons/home.png");
144
const assetsDir = zipFs.find("assets");
145
console.log(deepFile.getRelativeName(assetsDir)); // "images/icons/home.png"
146
```
147
148
#### isPasswordProtected()
149
150
Checks if the entry or any of its children requires a password.
151
152
```javascript
153
if (entry.isPasswordProtected()) {
154
const isValid = await entry.checkPassword("secret123");
155
if (isValid) {
156
// Password is correct
157
}
158
}
159
```
160
161
#### rename()
162
163
Changes the name of the entry.
164
165
```javascript
166
const file = zipFs.addText("old-name.txt", "content");
167
file.rename("new-name.txt");
168
console.log(file.getFullname()); // "new-name.txt"
169
```
170
171
## ZipFileEntry Class
172
173
Represents a file in the virtual filesystem.
174
175
```typescript { .api }
176
class ZipFileEntry<ReaderType, WriterType> extends ZipEntry {
177
directory: void;
178
reader: Reader<ReaderType> | ReadableReader | ReadableStream | (Reader<unknown> | ReadableReader | ReadableStream)[];
179
writer: Writer<WriterType> | WritableWriter | WritableStream | AsyncGenerator<Writer<unknown> | WritableWriter | WritableStream>;
180
181
getText(encoding?: string, options?: EntryGetDataOptions): Promise<string>;
182
getBlob(mimeType?: string, options?: EntryGetDataOptions): Promise<Blob>;
183
getData64URI(mimeType?: string, options?: EntryGetDataOptions): Promise<string>;
184
getUint8Array(options?: EntryGetDataOptions): Promise<Uint8Array>;
185
getWritable(writable?: WritableStream, options?: EntryGetDataOptions): Promise<WritableStream>;
186
getData<Type>(writer: Writer<unknown> | WritableWriter | WritableStream | AsyncGenerator<Writer<unknown> | WritableWriter | WritableStream>, options?: EntryGetDataOptions): Promise<Type>;
187
getArrayBuffer(options?: EntryGetDataOptions): Promise<ArrayBuffer>;
188
189
replaceBlob(blob: Blob): void;
190
replaceText(text: string): void;
191
replaceData64URI(dataURI: string): void;
192
replaceUint8Array(array: Uint8Array): void;
193
replaceReadable(readable: ReadableStream): void;
194
}
195
```
196
197
### Data Retrieval Methods
198
199
#### getText()
200
201
Retrieves file content as text.
202
203
```javascript
204
const textFile = zipFs.addText("config.json", '{"key": "value"}');
205
const content = await textFile.getText();
206
const config = JSON.parse(content);
207
```
208
209
#### getBlob()
210
211
Retrieves file content as a Blob.
212
213
```javascript
214
const imageFile = zipFs.addBlob("photo.jpg", photoBlob);
215
const retrievedBlob = await imageFile.getBlob("image/jpeg");
216
```
217
218
#### getData64URI()
219
220
Retrieves file content as a Base64 Data URI.
221
222
```javascript
223
const iconFile = zipFs.addBlob("icon.png", iconBlob);
224
const dataUri = await iconFile.getData64URI("image/png");
225
// Result: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
226
```
227
228
#### getUint8Array()
229
230
Retrieves file content as raw bytes.
231
232
```javascript
233
const binaryFile = zipFs.addBlob("data.bin", binaryBlob);
234
const bytes = await binaryFile.getUint8Array();
235
console.log(`File size: ${bytes.length} bytes`);
236
```
237
238
### Content Replacement Methods
239
240
#### replaceText()
241
242
Replaces file content with new text.
243
244
```javascript
245
const configFile = zipFs.addText("config.json", '{"version": 1}');
246
configFile.replaceText('{"version": 2}');
247
```
248
249
#### replaceBlob()
250
251
Replaces file content with a new Blob.
252
253
```javascript
254
const imageFile = zipFs.addBlob("old-image.png", oldBlob);
255
imageFile.replaceBlob(newImageBlob);
256
```
257
258
## ZipDirectoryEntry Class
259
260
Represents a directory in the virtual filesystem.
261
262
```typescript { .api }
263
class ZipDirectoryEntry extends ZipEntry {
264
directory: true;
265
266
getChildByName(name: string): ZipEntry | undefined;
267
268
// Adding entries
269
addDirectory(name: string, options?: ZipWriterAddDataOptions): ZipDirectoryEntry;
270
addText(name: string, text: string, options?: ZipWriterAddDataOptions): ZipFileEntry<string, string>;
271
addBlob(name: string, blob: Blob, options?: ZipWriterAddDataOptions): ZipFileEntry<Blob, Blob>;
272
addData64URI(name: string, dataURI: string, options?: ZipWriterAddDataOptions): ZipFileEntry<string, string>;
273
addUint8Array(name: string, array: Uint8Array, options?: ZipWriterAddDataOptions): ZipFileEntry<Uint8Array, Uint8Array>;
274
addHttpContent(name: string, url: string, options?: HttpOptions & ZipWriterAddDataOptions): ZipFileEntry<string, void>;
275
addReadable(name: string, readable: ReadableStream, options?: ZipWriterAddDataOptions): ZipFileEntry<ReadableStream, void>;
276
addFile(file: File, options?: ZipWriterAddDataOptions): Promise<ZipEntry>;
277
addFileSystemEntry(fileSystemEntry: FileSystemEntryLike, options?: ZipWriterAddDataOptions): Promise<ZipEntry[]>;
278
addFileSystemHandle(fileSystemHandle: FileSystemHandleLike, options?: ZipWriterAddDataOptions): Promise<ZipEntry[]>;
279
280
// Importing ZIP files
281
importBlob(blob: Blob, options?: ZipReaderConstructorOptions): Promise<[ZipEntry]>;
282
importData64URI(dataURI: string, options?: ZipReaderConstructorOptions): Promise<[ZipEntry]>;
283
importUint8Array(array: Uint8Array, options?: ZipReaderConstructorOptions): Promise<[ZipEntry]>;
284
importHttpContent(url: string, options?: ZipDirectoryEntryImportHttpOptions): Promise<[ZipEntry]>;
285
importReadable(readable: ReadableStream, options?: ZipReaderConstructorOptions): Promise<[ZipEntry]>;
286
importZip(reader: Reader<unknown> | ReadableReader | ReadableStream | (Reader<unknown> | ReadableReader | ReadableStream)[], options?: ZipReaderConstructorOptions): Promise<[ZipEntry]>;
287
288
// Exporting
289
exportBlob(options?: ZipDirectoryEntryExportOptions): Promise<Blob>;
290
exportData64URI(options?: ZipDirectoryEntryExportOptions): Promise<string>;
291
exportUint8Array(options?: ZipDirectoryEntryExportOptions): Promise<Uint8Array>;
292
exportWritable(writable?: WritableStream, options?: ZipDirectoryEntryExportOptions): Promise<WritableStream>;
293
exportZip(writer: Writer<unknown> | WritableWriter | WritableStream | AsyncGenerator<Writer<unknown> | WritableWriter | WritableStream>, options?: ZipDirectoryEntryExportOptions): Promise<unknown>;
294
}
295
```
296
297
### Navigation
298
299
#### getChildByName()
300
301
Finds a direct child by name.
302
303
```javascript
304
const assetsDir = zipFs.addDirectory("assets");
305
assetsDir.addText("style.css", "body {}");
306
307
const cssFile = assetsDir.getChildByName("style.css");
308
if (cssFile && !cssFile.directory) {
309
const content = await cssFile.getText();
310
}
311
```
312
313
### Adding Content
314
315
#### addDirectory()
316
317
Creates a new subdirectory.
318
319
```javascript
320
const projectDir = zipFs.addDirectory("my-project");
321
const srcDir = projectDir.addDirectory("src");
322
const testDir = projectDir.addDirectory("test");
323
```
324
325
#### addText()
326
327
Adds a text file.
328
329
```javascript
330
const srcDir = zipFs.addDirectory("src");
331
const mainFile = srcDir.addText("main.js", `
332
console.log("Hello, World!");
333
export default function main() {
334
// Application logic
335
}
336
`);
337
```
338
339
#### addBlob()
340
341
Adds a binary file from a Blob.
342
343
```javascript
344
const assetsDir = zipFs.addDirectory("assets");
345
const logoFile = assetsDir.addBlob("logo.png", logoBlob, {
346
lastModDate: new Date(),
347
comment: "Company logo"
348
});
349
```
350
351
#### addHttpContent()
352
353
Adds content fetched from a URL.
354
355
```javascript
356
const libsDir = zipFs.addDirectory("libs");
357
const jqueryFile = libsDir.addHttpContent(
358
"jquery.min.js",
359
"https://code.jquery.com/jquery-3.6.0.min.js",
360
{
361
headers: [["User-Agent", "MyApp/1.0"]]
362
}
363
);
364
```
365
366
#### addFile()
367
368
Adds content from a File object (from file input or drag & drop).
369
370
```javascript
371
// From file input
372
const fileInput = document.querySelector('input[type="file"]');
373
for (const file of fileInput.files) {
374
await zipFs.addFile(file);
375
}
376
```
377
378
### Importing ZIP Archives
379
380
#### importBlob()
381
382
Extracts a ZIP file into the directory.
383
384
```javascript
385
const backupDir = zipFs.addDirectory("backup");
386
await backupDir.importBlob(existingZipBlob);
387
// All entries from existingZipBlob are now in backup/
388
```
389
390
#### importHttpContent()
391
392
Downloads and extracts a ZIP file from a URL.
393
394
```javascript
395
const vendorDir = zipFs.addDirectory("vendor");
396
await vendorDir.importHttpContent(
397
"https://example.com/library-v1.2.3.zip"
398
);
399
```
400
401
### Exporting
402
403
#### exportBlob()
404
405
Exports the directory and its contents as a ZIP Blob.
406
407
```javascript
408
const projectDir = zipFs.addDirectory("my-project");
409
projectDir.addText("README.md", "# My Project");
410
projectDir.addText("package.json", '{"name": "my-project"}');
411
412
const zipBlob = await projectDir.exportBlob({
413
level: 9, // Maximum compression
414
mimeType: "application/zip"
415
});
416
```
417
418
#### exportData64URI()
419
420
Exports as a Base64 Data URI.
421
422
```javascript
423
const dataUri = await zipFs.exportData64URI();
424
// Can be used directly in download links
425
const downloadLink = document.createElement('a');
426
downloadLink.href = dataUri;
427
downloadLink.download = 'archive.zip';
428
```
429
430
## Complete Examples
431
432
### Building a Project Archive
433
434
```javascript
435
import { fs } from "@zip.js/zip.js";
436
437
async function createProjectArchive() {
438
const zipFs = new fs.FS();
439
440
// Create project structure
441
const projectDir = zipFs.addDirectory("my-web-app");
442
const srcDir = projectDir.addDirectory("src");
443
const assetsDir = projectDir.addDirectory("assets");
444
const docsDir = projectDir.addDirectory("docs");
445
446
// Add source files
447
srcDir.addText("index.html", `
448
<!DOCTYPE html>
449
<html>
450
<head><title>My Web App</title></head>
451
<body><h1>Hello World</h1></body>
452
</html>
453
`);
454
455
srcDir.addText("app.js", `
456
document.addEventListener('DOMContentLoaded', () => {
457
console.log('App loaded');
458
});
459
`);
460
461
srcDir.addText("style.css", `
462
body { font-family: Arial, sans-serif; }
463
h1 { color: #333; }
464
`);
465
466
// Add assets
467
await assetsDir.addHttpContent(
468
"jquery.min.js",
469
"https://code.jquery.com/jquery-3.6.0.min.js"
470
);
471
472
// Add documentation
473
docsDir.addText("README.md", `
474
# My Web App
475
476
A simple web application.
477
478
## Getting Started
479
480
Open index.html in a web browser.
481
`);
482
483
docsDir.addText("API.md", `
484
# API Documentation
485
486
## Functions
487
488
- \`init()\` - Initializes the application
489
`);
490
491
// Add project metadata
492
projectDir.addText("package.json", JSON.stringify({
493
name: "my-web-app",
494
version: "1.0.0",
495
description: "A simple web application",
496
main: "src/index.html"
497
}, null, 2));
498
499
// Export with compression
500
const zipBlob = await projectDir.exportBlob({
501
level: 6,
502
comment: "Project created on " + new Date().toISOString()
503
});
504
505
return zipBlob;
506
}
507
```
508
509
### Archive Management System
510
511
```javascript
512
import { fs } from "@zip.js/zip.js";
513
514
class ArchiveManager {
515
constructor() {
516
this.zipFs = new fs.FS();
517
}
518
519
async loadArchive(zipBlob) {
520
await this.zipFs.importBlob(zipBlob);
521
}
522
523
addFolder(path) {
524
const parts = path.split('/');
525
let currentDir = this.zipFs;
526
527
for (const part of parts) {
528
let child = currentDir.getChildByName(part);
529
if (!child) {
530
child = currentDir.addDirectory(part);
531
} else if (!child.directory) {
532
throw new Error(`Path conflict: ${part} is a file`);
533
}
534
currentDir = child;
535
}
536
537
return currentDir;
538
}
539
540
addTextFile(path, content) {
541
const parts = path.split('/');
542
const filename = parts.pop();
543
const dirPath = parts.join('/');
544
545
const dir = dirPath ? this.addFolder(dirPath) : this.zipFs;
546
return dir.addText(filename, content);
547
}
548
549
async addBinaryFile(path, blob) {
550
const parts = path.split('/');
551
const filename = parts.pop();
552
const dirPath = parts.join('/');
553
554
const dir = dirPath ? this.addFolder(dirPath) : this.zipFs;
555
return dir.addBlob(filename, blob);
556
}
557
558
findFile(path) {
559
return this.zipFs.find(path);
560
}
561
562
listFiles(directory = "") {
563
const dir = directory ? this.zipFs.find(directory) : this.zipFs;
564
if (!dir || !dir.directory) return [];
565
566
return dir.children.map(child => ({
567
name: child.name,
568
path: child.getFullname(),
569
isDirectory: child.directory,
570
size: child.uncompressedSize
571
}));
572
}
573
574
async moveFile(fromPath, toPath) {
575
const file = this.zipFs.find(fromPath);
576
if (!file) throw new Error(`File not found: ${fromPath}`);
577
578
const parts = toPath.split('/');
579
const filename = parts.pop();
580
const dirPath = parts.join('/');
581
582
const targetDir = dirPath ? this.addFolder(dirPath) : this.zipFs;
583
584
file.rename(filename);
585
this.zipFs.move(file, targetDir);
586
}
587
588
deleteFile(path) {
589
const file = this.zipFs.find(path);
590
if (file) {
591
this.zipFs.remove(file);
592
return true;
593
}
594
return false;
595
}
596
597
async exportArchive(options = {}) {
598
return await this.zipFs.exportBlob({
599
level: 6,
600
...options
601
});
602
}
603
604
async exportDirectory(path, options = {}) {
605
const dir = this.zipFs.find(path);
606
if (!dir || !dir.directory) {
607
throw new Error(`Directory not found: ${path}`);
608
}
609
return await dir.exportBlob(options);
610
}
611
}
612
613
// Usage
614
const manager = new ArchiveManager();
615
616
// Add files
617
manager.addTextFile("config/app.json", '{"debug": true}');
618
manager.addTextFile("src/main.js", "console.log('Hello');");
619
await manager.addBinaryFile("assets/logo.png", logoBlob);
620
621
// List contents
622
console.log(manager.listFiles()); // Root files
623
console.log(manager.listFiles("src")); // Files in src/
624
625
// Move file
626
await manager.moveFile("config/app.json", "config/production.json");
627
628
// Export specific directory
629
const srcZip = await manager.exportDirectory("src");
630
631
// Export entire archive
632
const fullZip = await manager.exportArchive({
633
level: 9,
634
comment: "Managed archive"
635
});
636
```
637
638
### Incremental Archive Building
639
640
```javascript
641
import { fs } from "@zip.js/zip.js";
642
643
class IncrementalArchiveBuilder {
644
constructor() {
645
this.zipFs = new fs.FS();
646
this.changes = new Set();
647
}
648
649
addContent(path, content, options = {}) {
650
const parts = path.split('/');
651
const filename = parts.pop();
652
653
let currentDir = this.zipFs;
654
for (const part of parts) {
655
let child = currentDir.getChildByName(part);
656
if (!child) {
657
child = currentDir.addDirectory(part);
658
}
659
currentDir = child;
660
}
661
662
let entry;
663
if (typeof content === 'string') {
664
entry = currentDir.addText(filename, content, options);
665
} else if (content instanceof Blob) {
666
entry = currentDir.addBlob(filename, content, options);
667
} else if (content instanceof Uint8Array) {
668
entry = currentDir.addUint8Array(filename, content, options);
669
}
670
671
this.changes.add(path);
672
return entry;
673
}
674
675
updateContent(path, newContent) {
676
const entry = this.zipFs.find(path);
677
if (!entry || entry.directory) {
678
throw new Error(`File not found: ${path}`);
679
}
680
681
if (typeof newContent === 'string') {
682
entry.replaceText(newContent);
683
} else if (newContent instanceof Blob) {
684
entry.replaceBlob(newContent);
685
} else if (newContent instanceof Uint8Array) {
686
entry.replaceUint8Array(newContent);
687
}
688
689
this.changes.add(path);
690
}
691
692
getChangedFiles() {
693
return Array.from(this.changes);
694
}
695
696
async createSnapshot() {
697
const snapshot = await this.zipFs.exportBlob({
698
level: 6,
699
comment: `Snapshot created ${new Date().toISOString()}`
700
});
701
702
this.changes.clear();
703
return snapshot;
704
}
705
706
async createDelta(basePath = null) {
707
if (this.changes.size === 0) return null;
708
709
const deltaFs = new fs.FS();
710
711
for (const changedPath of this.changes) {
712
const entry = this.zipFs.find(changedPath);
713
if (entry && !entry.directory) {
714
const data = await entry.getData(new Uint8ArrayWriter());
715
deltaFs.addUint8Array(changedPath, data);
716
}
717
}
718
719
return await deltaFs.exportBlob({
720
level: 9,
721
comment: `Delta with ${this.changes.size} changes`
722
});
723
}
724
}
725
726
// Usage
727
const builder = new IncrementalArchiveBuilder();
728
729
// Build archive incrementally
730
builder.addContent("v1/app.js", "console.log('v1');");
731
builder.addContent("v1/config.json", '{"version": 1}');
732
733
const v1Snapshot = await builder.createSnapshot();
734
735
// Make changes
736
builder.updateContent("v1/app.js", "console.log('v1.1');");
737
builder.addContent("v1/patch.js", "console.log('patch');");
738
739
const delta = await builder.createDelta();
740
const v1_1Snapshot = await builder.createSnapshot();
741
742
console.log("Changed files:", builder.getChangedFiles());
743
```
744
745
The Filesystem API provides a powerful and intuitive way to work with ZIP files as virtual filesystems, making complex archive operations simple and maintainable.