or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mddata-io.mdfilesystem.mdindex.mdreading.mdstreaming.mdwriting.md

writing.mddocs/

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

```