CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-zip-js--zip-js

JavaScript library for compressing and decompressing ZIP files in browsers, Node.js, and Deno with support for encryption, ZIP64, and web workers

Overview
Eval results
Files

filesystem.mddocs/

Filesystem API

High-level filesystem-like operations for complex ZIP manipulation, providing an intuitive interface for managing ZIP archives as virtual filesystems.

fs Object

The main entry point for the filesystem API.

const fs: {
  FS: typeof FS;
  ZipDirectoryEntry: typeof ZipDirectoryEntry;
  ZipFileEntry: typeof ZipFileEntry;
};

Usage

import { fs } from "@zip.js/zip.js";

// Create a new filesystem
const zipFs = new fs.FS();

// Add files and directories
zipFs.addText("readme.txt", "Welcome!");
const assetsDir = zipFs.addDirectory("assets");
assetsDir.addBlob("logo.png", logoBlob);

// Export as ZIP
const zipBlob = await zipFs.exportBlob();

FS Class

The root filesystem class that manages ZIP entries as a virtual filesystem.

class FS extends ZipDirectoryEntry {
  readonly root: ZipDirectoryEntry;
  
  remove(entry: ZipEntry): void;
  move(entry: ZipEntry, destination: ZipDirectoryEntry): void;
  find(fullname: string): ZipEntry | undefined;
  getById(id: number): ZipEntry | undefined;
}

Properties

  • root: The root directory entry

Methods

remove()

Removes an entry and all its children from the filesystem.

const fileEntry = zipFs.addText("temp.txt", "temporary");
zipFs.remove(fileEntry); // File is removed from filesystem

move()

Moves an entry to a different directory.

const file = zipFs.addText("misplaced.txt", "content");
const targetDir = zipFs.addDirectory("correct-location");
zipFs.move(file, targetDir); // Now at "correct-location/misplaced.txt"

find()

Finds an entry by its full path.

const entry = zipFs.find("assets/images/logo.png");
if (entry && !entry.directory) {
  const blob = await entry.getBlob();
}

getById()

Finds an entry by its unique ID.

const entry = zipFs.getById(123);
if (entry) {
  console.log(`Found: ${entry.getFullname()}`);
}

ZipEntry Base Class

Base class for all filesystem entries.

class ZipEntry {
  name: string;
  data?: EntryMetaData;
  id: number;
  parent?: ZipEntry;
  uncompressedSize: number;
  children: ZipEntry[];
  
  clone(deepClone?: boolean): ZipEntry;
  getFullname(): string;
  getRelativeName(ancestor: ZipDirectoryEntry): string;
  isDescendantOf(ancestor: ZipDirectoryEntry): boolean;
  isPasswordProtected(): boolean;
  checkPassword(password: string, options?: EntryGetDataOptions): Promise<boolean>;
  rename(name: string): void;
}

Properties

  • name: Entry name (without path)
  • data: Associated metadata from ZIP file
  • id: Unique identifier
  • parent: Parent directory
  • uncompressedSize: Size of uncompressed content
  • children: Child entries (for directories)

Methods

getFullname()

Returns the complete path of the entry.

const file = assetsDir.addText("style.css", "body {}");
console.log(file.getFullname()); // "assets/style.css"

getRelativeName()

Returns the path relative to an ancestor directory.

const deepFile = zipFs.find("assets/images/icons/home.png");
const assetsDir = zipFs.find("assets");
console.log(deepFile.getRelativeName(assetsDir)); // "images/icons/home.png"

isPasswordProtected()

Checks if the entry or any of its children requires a password.

if (entry.isPasswordProtected()) {
  const isValid = await entry.checkPassword("secret123");
  if (isValid) {
    // Password is correct
  }
}

rename()

Changes the name of the entry.

const file = zipFs.addText("old-name.txt", "content");
file.rename("new-name.txt");
console.log(file.getFullname()); // "new-name.txt"

ZipFileEntry Class

Represents a file in the virtual filesystem.

class ZipFileEntry<ReaderType, WriterType> extends ZipEntry {
  directory: void;
  reader: Reader<ReaderType> | ReadableReader | ReadableStream | (Reader<unknown> | ReadableReader | ReadableStream)[];
  writer: Writer<WriterType> | WritableWriter | WritableStream | AsyncGenerator<Writer<unknown> | WritableWriter | WritableStream>;
  
  getText(encoding?: string, options?: EntryGetDataOptions): Promise<string>;
  getBlob(mimeType?: string, options?: EntryGetDataOptions): Promise<Blob>;
  getData64URI(mimeType?: string, options?: EntryGetDataOptions): Promise<string>;
  getUint8Array(options?: EntryGetDataOptions): Promise<Uint8Array>;
  getWritable(writable?: WritableStream, options?: EntryGetDataOptions): Promise<WritableStream>;
  getData<Type>(writer: Writer<unknown> | WritableWriter | WritableStream | AsyncGenerator<Writer<unknown> | WritableWriter | WritableStream>, options?: EntryGetDataOptions): Promise<Type>;
  getArrayBuffer(options?: EntryGetDataOptions): Promise<ArrayBuffer>;
  
  replaceBlob(blob: Blob): void;
  replaceText(text: string): void;
  replaceData64URI(dataURI: string): void;
  replaceUint8Array(array: Uint8Array): void;
  replaceReadable(readable: ReadableStream): void;
}

Data Retrieval Methods

getText()

Retrieves file content as text.

const textFile = zipFs.addText("config.json", '{"key": "value"}');
const content = await textFile.getText();
const config = JSON.parse(content);

getBlob()

Retrieves file content as a Blob.

const imageFile = zipFs.addBlob("photo.jpg", photoBlob);
const retrievedBlob = await imageFile.getBlob("image/jpeg");

getData64URI()

Retrieves file content as a Base64 Data URI.

const iconFile = zipFs.addBlob("icon.png", iconBlob);
const dataUri = await iconFile.getData64URI("image/png");
// Result: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."

getUint8Array()

Retrieves file content as raw bytes.

const binaryFile = zipFs.addBlob("data.bin", binaryBlob);
const bytes = await binaryFile.getUint8Array();
console.log(`File size: ${bytes.length} bytes`);

Content Replacement Methods

replaceText()

Replaces file content with new text.

const configFile = zipFs.addText("config.json", '{"version": 1}');
configFile.replaceText('{"version": 2}');

replaceBlob()

Replaces file content with a new Blob.

const imageFile = zipFs.addBlob("old-image.png", oldBlob);
imageFile.replaceBlob(newImageBlob);

ZipDirectoryEntry Class

Represents a directory in the virtual filesystem.

class ZipDirectoryEntry extends ZipEntry {
  directory: true;
  
  getChildByName(name: string): ZipEntry | undefined;
  
  // Adding entries
  addDirectory(name: string, options?: ZipWriterAddDataOptions): ZipDirectoryEntry;
  addText(name: string, text: string, options?: ZipWriterAddDataOptions): ZipFileEntry<string, string>;
  addBlob(name: string, blob: Blob, options?: ZipWriterAddDataOptions): ZipFileEntry<Blob, Blob>;
  addData64URI(name: string, dataURI: string, options?: ZipWriterAddDataOptions): ZipFileEntry<string, string>;
  addUint8Array(name: string, array: Uint8Array, options?: ZipWriterAddDataOptions): ZipFileEntry<Uint8Array, Uint8Array>;
  addHttpContent(name: string, url: string, options?: HttpOptions & ZipWriterAddDataOptions): ZipFileEntry<string, void>;
  addReadable(name: string, readable: ReadableStream, options?: ZipWriterAddDataOptions): ZipFileEntry<ReadableStream, void>;
  addFile(file: File, options?: ZipWriterAddDataOptions): Promise<ZipEntry>;
  addFileSystemEntry(fileSystemEntry: FileSystemEntryLike, options?: ZipWriterAddDataOptions): Promise<ZipEntry[]>;
  addFileSystemHandle(fileSystemHandle: FileSystemHandleLike, options?: ZipWriterAddDataOptions): Promise<ZipEntry[]>;
  
  // Importing ZIP files
  importBlob(blob: Blob, options?: ZipReaderConstructorOptions): Promise<[ZipEntry]>;
  importData64URI(dataURI: string, options?: ZipReaderConstructorOptions): Promise<[ZipEntry]>;
  importUint8Array(array: Uint8Array, options?: ZipReaderConstructorOptions): Promise<[ZipEntry]>;
  importHttpContent(url: string, options?: ZipDirectoryEntryImportHttpOptions): Promise<[ZipEntry]>;
  importReadable(readable: ReadableStream, options?: ZipReaderConstructorOptions): Promise<[ZipEntry]>;
  importZip(reader: Reader<unknown> | ReadableReader | ReadableStream | (Reader<unknown> | ReadableReader | ReadableStream)[], options?: ZipReaderConstructorOptions): Promise<[ZipEntry]>;
  
  // Exporting
  exportBlob(options?: ZipDirectoryEntryExportOptions): Promise<Blob>;
  exportData64URI(options?: ZipDirectoryEntryExportOptions): Promise<string>;
  exportUint8Array(options?: ZipDirectoryEntryExportOptions): Promise<Uint8Array>;
  exportWritable(writable?: WritableStream, options?: ZipDirectoryEntryExportOptions): Promise<WritableStream>;
  exportZip(writer: Writer<unknown> | WritableWriter | WritableStream | AsyncGenerator<Writer<unknown> | WritableWriter | WritableStream>, options?: ZipDirectoryEntryExportOptions): Promise<unknown>;
}

Navigation

getChildByName()

Finds a direct child by name.

const assetsDir = zipFs.addDirectory("assets");
assetsDir.addText("style.css", "body {}");

const cssFile = assetsDir.getChildByName("style.css");
if (cssFile && !cssFile.directory) {
  const content = await cssFile.getText();
}

Adding Content

addDirectory()

Creates a new subdirectory.

const projectDir = zipFs.addDirectory("my-project");
const srcDir = projectDir.addDirectory("src");
const testDir = projectDir.addDirectory("test");

addText()

Adds a text file.

const srcDir = zipFs.addDirectory("src");
const mainFile = srcDir.addText("main.js", `
  console.log("Hello, World!");
  export default function main() {
    // Application logic
  }
`);

addBlob()

Adds a binary file from a Blob.

const assetsDir = zipFs.addDirectory("assets");
const logoFile = assetsDir.addBlob("logo.png", logoBlob, {
  lastModDate: new Date(),
  comment: "Company logo"
});

addHttpContent()

Adds content fetched from a URL.

const libsDir = zipFs.addDirectory("libs");
const jqueryFile = libsDir.addHttpContent(
  "jquery.min.js",
  "https://code.jquery.com/jquery-3.6.0.min.js",
  {
    headers: [["User-Agent", "MyApp/1.0"]]
  }
);

addFile()

Adds content from a File object (from file input or drag & drop).

// From file input
const fileInput = document.querySelector('input[type="file"]');
for (const file of fileInput.files) {
  await zipFs.addFile(file);
}

Importing ZIP Archives

importBlob()

Extracts a ZIP file into the directory.

const backupDir = zipFs.addDirectory("backup");
await backupDir.importBlob(existingZipBlob);
// All entries from existingZipBlob are now in backup/

importHttpContent()

Downloads and extracts a ZIP file from a URL.

const vendorDir = zipFs.addDirectory("vendor");
await vendorDir.importHttpContent(
  "https://example.com/library-v1.2.3.zip"
);

Exporting

exportBlob()

Exports the directory and its contents as a ZIP Blob.

const projectDir = zipFs.addDirectory("my-project");
projectDir.addText("README.md", "# My Project");
projectDir.addText("package.json", '{"name": "my-project"}');

const zipBlob = await projectDir.exportBlob({
  level: 9, // Maximum compression
  mimeType: "application/zip"
});

exportData64URI()

Exports as a Base64 Data URI.

const dataUri = await zipFs.exportData64URI();
// Can be used directly in download links
const downloadLink = document.createElement('a');
downloadLink.href = dataUri;
downloadLink.download = 'archive.zip';

Complete Examples

Building a Project Archive

import { fs } from "@zip.js/zip.js";

async function createProjectArchive() {
  const zipFs = new fs.FS();
  
  // Create project structure
  const projectDir = zipFs.addDirectory("my-web-app");
  const srcDir = projectDir.addDirectory("src");
  const assetsDir = projectDir.addDirectory("assets");
  const docsDir = projectDir.addDirectory("docs");
  
  // Add source files
  srcDir.addText("index.html", `
    <!DOCTYPE html>
    <html>
      <head><title>My Web App</title></head>
      <body><h1>Hello World</h1></body>
    </html>
  `);
  
  srcDir.addText("app.js", `
    document.addEventListener('DOMContentLoaded', () => {
      console.log('App loaded');
    });
  `);
  
  srcDir.addText("style.css", `
    body { font-family: Arial, sans-serif; }
    h1 { color: #333; }
  `);
  
  // Add assets
  await assetsDir.addHttpContent(
    "jquery.min.js",
    "https://code.jquery.com/jquery-3.6.0.min.js"
  );
  
  // Add documentation
  docsDir.addText("README.md", `
    # My Web App
    
    A simple web application.
    
    ## Getting Started
    
    Open index.html in a web browser.
  `);
  
  docsDir.addText("API.md", `
    # API Documentation
    
    ## Functions
    
    - \`init()\` - Initializes the application
  `);
  
  // Add project metadata
  projectDir.addText("package.json", JSON.stringify({
    name: "my-web-app",
    version: "1.0.0",
    description: "A simple web application",
    main: "src/index.html"
  }, null, 2));
  
  // Export with compression
  const zipBlob = await projectDir.exportBlob({
    level: 6,
    comment: "Project created on " + new Date().toISOString()
  });
  
  return zipBlob;
}

Archive Management System

import { fs } from "@zip.js/zip.js";

class ArchiveManager {
  constructor() {
    this.zipFs = new fs.FS();
  }
  
  async loadArchive(zipBlob) {
    await this.zipFs.importBlob(zipBlob);
  }
  
  addFolder(path) {
    const parts = path.split('/');
    let currentDir = this.zipFs;
    
    for (const part of parts) {
      let child = currentDir.getChildByName(part);
      if (!child) {
        child = currentDir.addDirectory(part);
      } else if (!child.directory) {
        throw new Error(`Path conflict: ${part} is a file`);
      }
      currentDir = child;
    }
    
    return currentDir;
  }
  
  addTextFile(path, content) {
    const parts = path.split('/');
    const filename = parts.pop();
    const dirPath = parts.join('/');
    
    const dir = dirPath ? this.addFolder(dirPath) : this.zipFs;
    return dir.addText(filename, content);
  }
  
  async addBinaryFile(path, blob) {
    const parts = path.split('/');
    const filename = parts.pop();
    const dirPath = parts.join('/');
    
    const dir = dirPath ? this.addFolder(dirPath) : this.zipFs;
    return dir.addBlob(filename, blob);
  }
  
  findFile(path) {
    return this.zipFs.find(path);
  }
  
  listFiles(directory = "") {
    const dir = directory ? this.zipFs.find(directory) : this.zipFs;
    if (!dir || !dir.directory) return [];
    
    return dir.children.map(child => ({
      name: child.name,
      path: child.getFullname(),
      isDirectory: child.directory,
      size: child.uncompressedSize
    }));
  }
  
  async moveFile(fromPath, toPath) {
    const file = this.zipFs.find(fromPath);
    if (!file) throw new Error(`File not found: ${fromPath}`);
    
    const parts = toPath.split('/');
    const filename = parts.pop();
    const dirPath = parts.join('/');
    
    const targetDir = dirPath ? this.addFolder(dirPath) : this.zipFs;
    
    file.rename(filename);
    this.zipFs.move(file, targetDir);
  }
  
  deleteFile(path) {
    const file = this.zipFs.find(path);
    if (file) {
      this.zipFs.remove(file);
      return true;
    }
    return false;
  }
  
  async exportArchive(options = {}) {
    return await this.zipFs.exportBlob({
      level: 6,
      ...options
    });
  }
  
  async exportDirectory(path, options = {}) {
    const dir = this.zipFs.find(path);
    if (!dir || !dir.directory) {
      throw new Error(`Directory not found: ${path}`);
    }
    return await dir.exportBlob(options);
  }
}

// Usage
const manager = new ArchiveManager();

// Add files
manager.addTextFile("config/app.json", '{"debug": true}');
manager.addTextFile("src/main.js", "console.log('Hello');");
await manager.addBinaryFile("assets/logo.png", logoBlob);

// List contents
console.log(manager.listFiles()); // Root files
console.log(manager.listFiles("src")); // Files in src/

// Move file
await manager.moveFile("config/app.json", "config/production.json");

// Export specific directory
const srcZip = await manager.exportDirectory("src");

// Export entire archive
const fullZip = await manager.exportArchive({
  level: 9,
  comment: "Managed archive"
});

Incremental Archive Building

import { fs } from "@zip.js/zip.js";

class IncrementalArchiveBuilder {
  constructor() {
    this.zipFs = new fs.FS();
    this.changes = new Set();
  }
  
  addContent(path, content, options = {}) {
    const parts = path.split('/');
    const filename = parts.pop();
    
    let currentDir = this.zipFs;
    for (const part of parts) {
      let child = currentDir.getChildByName(part);
      if (!child) {
        child = currentDir.addDirectory(part);
      }
      currentDir = child;
    }
    
    let entry;
    if (typeof content === 'string') {
      entry = currentDir.addText(filename, content, options);
    } else if (content instanceof Blob) {
      entry = currentDir.addBlob(filename, content, options);
    } else if (content instanceof Uint8Array) {
      entry = currentDir.addUint8Array(filename, content, options);
    }
    
    this.changes.add(path);
    return entry;
  }
  
  updateContent(path, newContent) {
    const entry = this.zipFs.find(path);
    if (!entry || entry.directory) {
      throw new Error(`File not found: ${path}`);
    }
    
    if (typeof newContent === 'string') {
      entry.replaceText(newContent);
    } else if (newContent instanceof Blob) {
      entry.replaceBlob(newContent);
    } else if (newContent instanceof Uint8Array) {
      entry.replaceUint8Array(newContent);
    }
    
    this.changes.add(path);
  }
  
  getChangedFiles() {
    return Array.from(this.changes);
  }
  
  async createSnapshot() {
    const snapshot = await this.zipFs.exportBlob({
      level: 6,
      comment: `Snapshot created ${new Date().toISOString()}`
    });
    
    this.changes.clear();
    return snapshot;
  }
  
  async createDelta(basePath = null) {
    if (this.changes.size === 0) return null;
    
    const deltaFs = new fs.FS();
    
    for (const changedPath of this.changes) {
      const entry = this.zipFs.find(changedPath);
      if (entry && !entry.directory) {
        const data = await entry.getData(new Uint8ArrayWriter());
        deltaFs.addUint8Array(changedPath, data);
      }
    }
    
    return await deltaFs.exportBlob({
      level: 9,
      comment: `Delta with ${this.changes.size} changes`
    });
  }
}

// Usage
const builder = new IncrementalArchiveBuilder();

// Build archive incrementally
builder.addContent("v1/app.js", "console.log('v1');");
builder.addContent("v1/config.json", '{"version": 1}');

const v1Snapshot = await builder.createSnapshot();

// Make changes
builder.updateContent("v1/app.js", "console.log('v1.1');");
builder.addContent("v1/patch.js", "console.log('patch');");

const delta = await builder.createDelta();
const v1_1Snapshot = await builder.createSnapshot();

console.log("Changed files:", builder.getChangedFiles());

The Filesystem API provides a powerful and intuitive way to work with ZIP files as virtual filesystems, making complex archive operations simple and maintainable.

Install with Tessl CLI

npx tessl i tessl/npm-zip-js--zip-js

docs

configuration.md

data-io.md

filesystem.md

index.md

reading.md

streaming.md

writing.md

tile.json