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

writing.mddocs/

ZIP Writing

Creating and modifying ZIP files with support for compression, encryption, and various data sources.

ZipWriter Class

The main class for creating ZIP files.

class ZipWriter<Type> {
  constructor(
    writer: Writer<Type> | WritableWriter | WritableStream | AsyncGenerator<Writer<unknown> | WritableWriter | WritableStream, boolean>,
    options?: ZipWriterConstructorOptions
  );
  
  readonly hasCorruptedEntries?: boolean;
  
  prependZip<ReaderType>(
    reader: Reader<ReaderType> | ReadableReader | ReadableStream | (Reader<unknown> | ReadableReader | ReadableStream)[]
  ): Promise<void>;
  
  add<ReaderType>(
    filename: string,
    reader?: Reader<ReaderType> | ReadableReader | ReadableStream | (Reader<unknown> | ReadableReader | ReadableStream)[],
    options?: ZipWriterAddDataOptions
  ): Promise<EntryMetaData>;
  
  remove(entry: Entry | string): boolean;
  close(comment?: Uint8Array, options?: ZipWriterCloseOptions): Promise<Type>;
}

Constructor Parameters

  • writer: Destination for the ZIP file. Can be a Writer instance, WritableStream, or generator for split archives
  • options: Configuration options for writing behavior

Properties

  • hasCorruptedEntries: true if any entries were partially written (indicates corruption)

Methods

add()

Adds a new entry to the ZIP file.

// Add text file
await zipWriter.add("hello.txt", new TextReader("Hello World!"));

// Add binary file
await zipWriter.add("image.png", new BlobReader(imageBlob));

// Add directory
await zipWriter.add("folder/", null, { directory: true });

// Add with options
await zipWriter.add("secret.txt", new TextReader("Secret data"), {
  password: "secret123",
  level: 9,
  lastModDate: new Date()
});

prependZip()

Adds an existing ZIP file at the beginning of the current ZIP. Must be called before any add() calls.

const existingZipReader = new ZipReader(new BlobReader(existingZipBlob));
await zipWriter.prependZip(existingZipReader);
// Now add new entries
await zipWriter.add("new-file.txt", new TextReader("New content"));

remove()

Marks an entry for removal (entry won't be written to the final ZIP).

const entryMetadata = await zipWriter.add("temp.txt", new TextReader("temp"));
const removed = zipWriter.remove(entryMetadata); // or remove("temp.txt")
console.log(removed); // true if entry was removed

close()

Finalizes the ZIP file by writing the central directory and closing the writer.

const zipBlob = await zipWriter.close();
// Optional global comment
const zipBlobWithComment = await zipWriter.close(
  new TextEncoder().encode("Created by my app")
);

Writing Options

ZipWriterConstructorOptions

interface ZipWriterConstructorOptions {
  zip64?: boolean;
  preventClose?: boolean;
  level?: number;
  bufferedWrite?: boolean;
  keepOrder?: boolean;
  password?: string;
  rawPassword?: Uint8Array;
  encryptionStrength?: 1 | 2 | 3;
  signal?: AbortSignal;
  lastModDate?: Date;
  lastAccessDate?: Date;
  creationDate?: Date;
  extendedTimestamp?: boolean;
  zipCrypto?: boolean;
  version?: number;
  versionMadeBy?: number;
  useUnicodeFileNames?: boolean;
  dataDescriptor?: boolean;
  dataDescriptorSignature?: boolean;
  msDosCompatible?: boolean;
  externalFileAttributes?: number;
  internalFileAttributes?: number;
  supportZip64SplitFile?: boolean;
  usdz?: boolean;
  passThrough?: boolean;
  encrypted?: boolean;
  offset?: number;
  compressionMethod?: number;
  encodeText?(text: string): Uint8Array | undefined;
}

Key Options

  • level: Compression level 0-9 (0=no compression, 9=maximum compression, default: 6)
  • password: Default password for encrypting entries
  • encryptionStrength: AES encryption strength (1=128-bit, 2=192-bit, 3=256-bit, default: 3)
  • zip64: Force ZIP64 format for large files (>4GB) or many entries (>65535)
  • keepOrder: Maintain entry order in the file (improves web worker performance)
  • bufferedWrite: Buffer entry data before writing (required for some scenarios)
  • useUnicodeFileNames: Mark filenames as UTF-8 encoded
  • lastModDate: Default last modification date for entries
  • extendedTimestamp: Store extended timestamp information (access/creation dates)
  • zipCrypto: Use legacy ZipCrypto encryption (not recommended, use AES instead)
  • usdz: Create USDZ-compatible ZIP files

ZipWriterAddDataOptions

interface ZipWriterAddDataOptions extends ZipWriterConstructorOptions, EntryDataOnprogressOptions, WorkerConfiguration {
  directory?: boolean;
  executable?: boolean;
  comment?: string;
  extraField?: Map<number, Uint8Array>;
  uncompressedSize?: number;
  signature?: number;
}

Entry-specific options that override constructor defaults:

  • directory: true to create a directory entry
  • executable: true to mark file as executable
  • comment: Entry-specific comment
  • extraField: Custom extra field data
  • uncompressedSize: Specify size if reader doesn't provide it
  • signature: CRC32 signature if known in advance

ZipWriterCloseOptions

interface ZipWriterCloseOptions extends EntryOnprogressOptions {
  zip64?: boolean;
  preventClose?: boolean;
}
  • zip64: Force ZIP64 format for central directory
  • preventClose: Don't close the underlying writer stream

Usage Examples

Basic ZIP Creation

import { ZipWriter, BlobWriter, TextReader, BlobReader } from "@zip.js/zip.js";

const zipWriter = new ZipWriter(new BlobWriter("application/zip"));

// Add text file
await zipWriter.add("readme.txt", new TextReader("Welcome to my app!"));

// Add binary file
await zipWriter.add("logo.png", new BlobReader(logoBlob));

// Add directory
await zipWriter.add("assets/", null, { directory: true });
await zipWriter.add("assets/style.css", new TextReader("body { margin: 0; }"));

// Finalize ZIP
const zipBlob = await zipWriter.close();

Encrypted ZIP Creation

const zipWriter = new ZipWriter(new BlobWriter("application/zip"), {
  password: "secret123",
  encryptionStrength: 3 // 256-bit AES
});

await zipWriter.add("confidential.txt", new TextReader("Top secret data"));

// Override password for specific entry
await zipWriter.add("public.txt", new TextReader("Public data"), {
  password: undefined // No encryption for this entry
});

const encryptedZip = await zipWriter.close();

High Compression ZIP

const zipWriter = new ZipWriter(new BlobWriter("application/zip"), {
  level: 9, // Maximum compression
  keepOrder: true // Better performance with web workers
});

// Add large text files that compress well
await zipWriter.add("large-log.txt", new BlobReader(logFileBlob));
await zipWriter.add("data.json", new TextReader(JSON.stringify(largeDataObject)));

const compressedZip = await zipWriter.close();

ZIP with Custom Metadata

const zipWriter = new ZipWriter(new BlobWriter("application/zip"), {
  extendedTimestamp: true,
  useUnicodeFileNames: true
});

const customDate = new Date('2023-01-01T12:00:00Z');

await zipWriter.add("document.txt", new TextReader("Content"), {
  lastModDate: customDate,
  lastAccessDate: customDate,
  creationDate: customDate,
  comment: "Important document",
  executable: false
});

const zipWithMetadata = await zipWriter.close(
  new TextEncoder().encode("Archive created on " + new Date().toISOString())
);

Split ZIP Creation

// Create split ZIP files (useful for size limits)
async function* createSplitWriters() {
  let partNumber = 1;
  while (partNumber < 10) { // Max 10 parts
    const writer = new BlobWriter("application/zip");
    writer.maxSize = 1024 * 1024; // 1MB per part
    yield writer;
    partNumber++;
  }
  return true; // Signal completion
}

const zipWriter = new ZipWriter(createSplitWriters());

// Add files normally - they'll be split automatically
await zipWriter.add("large-file1.dat", new BlobReader(largeBlob1));
await zipWriter.add("large-file2.dat", new BlobReader(largeBlob2));

await zipWriter.close();

Progress Tracking

const zipWriter = new ZipWriter(new BlobWriter("application/zip"));

await zipWriter.add("large-file.bin", new BlobReader(largeBlob), {
  onstart: (total) => {
    console.log(`Starting compression of ${total} bytes`);
  },
  onprogress: (progress, total) => {
    const percent = Math.round(progress / total * 100);
    console.log(`Compression progress: ${percent}%`);
  },
  onend: (computedSize) => {
    console.log(`Compression completed: ${computedSize} bytes processed`);
  }
});

const zipBlob = await zipWriter.close({
  onprogress: (progress, total, entry) => {
    console.log(`Writing central directory: entry ${progress}/${total} (${entry.filename})`);
  }
});

Streaming from HTTP

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

const zipWriter = new ZipWriter(new BlobWriter("application/zip"));

// Add file directly from URL without downloading to memory first
await zipWriter.add(
  "remote-file.pdf", 
  new HttpReader("https://example.com/document.pdf")
);

await zipWriter.add(
  "another-remote-file.jpg",
  new HttpReader("https://example.com/image.jpg", {
    headers: [["Authorization", "Bearer token123"]]
  })
);

const zipBlob = await zipWriter.close();

Modifying Existing ZIP

// Read existing ZIP
const existingZipReader = new ZipReader(new BlobReader(existingZipBlob));
const existingEntries = await existingZipReader.getEntries();

// Create new ZIP with modifications
const zipWriter = new ZipWriter(new BlobWriter("application/zip"));

// Copy existing entries (except ones to modify)
for (const entry of existingEntries) {
  if (entry.filename !== "file-to-replace.txt") {
    await zipWriter.add(entry.filename, 
      entry.directory ? null : entry.getData(new Uint8ArrayReader(await entry.getData(new Uint8ArrayWriter())))
    );
  }
}

// Add new/replacement entries
await zipWriter.add("file-to-replace.txt", new TextReader("New content"));
await zipWriter.add("new-file.txt", new TextReader("Additional content"));

await existingZipReader.close();
const modifiedZip = await zipWriter.close();

USDZ Creation (3D Assets)

// Create USDZ file (ZIP-based format for 3D assets)
const zipWriter = new ZipWriter(new BlobWriter("model/vnd.usdz+zip"), {
  usdz: true, // USDZ-specific formatting
  level: 0,   // USDZ typically uses no compression
  useUnicodeFileNames: false
});

await zipWriter.add("scene.usdc", new BlobReader(sceneBlob));
await zipWriter.add("textures/diffuse.jpg", new BlobReader(textureBlob));

const usdzBlob = await zipWriter.close();

Performance Optimization

// Optimize for large ZIP creation
const zipWriter = new ZipWriter(new BlobWriter("application/zip"), {
  level: 6,           // Balanced compression
  keepOrder: true,    // Better web worker utilization
  bufferedWrite: false, // Stream directly for better memory usage
  useWebWorkers: true,  // Enable parallel compression
  useCompressionStream: true // Use native compression if available
});

// Add files in order of increasing size for better parallelization
const files = [smallFile1, smallFile2, mediumFile1, largeFile1];
for (const file of files) {
  await zipWriter.add(file.name, new BlobReader(file.blob));
}

const optimizedZip = await zipWriter.close();

Error Handling

import {
  ERR_DUPLICATED_NAME,
  ERR_INVALID_ENTRY_NAME,
  ERR_ZIP_NOT_EMPTY
} from "@zip.js/zip.js";

try {
  const zipWriter = new ZipWriter(new BlobWriter("application/zip"));
  
  await zipWriter.add("file.txt", new TextReader("content"));
  
  // This will throw ERR_DUPLICATED_NAME
  await zipWriter.add("file.txt", new TextReader("different content"));
  
} catch (error) {
  if (error.message === ERR_DUPLICATED_NAME) {
    console.error("Duplicate filename in ZIP");
  } else if (error.message === ERR_INVALID_ENTRY_NAME) {
    console.error("Invalid entry name");
  } else {
    console.error("Error creating ZIP:", error);
  }
}

Advanced Features

Custom Compression

// Use custom compression codec
import { configure } from "@zip.js/zip.js";

configure({
  Deflate: CustomDeflateClass,
  Inflate: CustomInflateClass
});

const zipWriter = new ZipWriter(new BlobWriter("application/zip"));
// ZIP will use custom compression

Custom Worker Scripts

configure({
  workerScripts: {
    deflate: ["./custom-deflate-worker.js"],
    inflate: ["./custom-inflate-worker.js"]
  }
});

Memory Management

// For very large ZIP files, use streaming and limit workers
configure({
  maxWorkers: 2, // Limit concurrent workers
  terminateWorkerTimeout: 1000 // Terminate workers quickly
});

const zipWriter = new ZipWriter(new BlobWriter("application/zip"), {
  bufferedWrite: false, // Stream directly to reduce memory usage
  keepOrder: false      // Allow out-of-order writing for memory efficiency
});

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