koni_archive 0.6.0 copy "koni_archive: ^0.6.0" to clipboard
koni_archive: ^0.6.0 copied to clipboard

Format-agnostic archive reading and writing for Dart — one streaming-first API over ZIP, TAR, GZIP, 7z, and RAR. Facade of the koni_archive ecosystem.

koni_archive #

Format-agnostic archive reading and writing for pure Dart — one streaming-first API that reads ZIP/CBZ, TAR/CBT, GZIP (incl. .tar.gz), 7z/CB7, and RAR/CBR, and writes ZIP, TAR, and 7z (7z with a pure-Dart LZMA/LZMA2 encoder). No native code, no FFI, no external executables. Runs everywhere Dart runs: the VM, Flutter (all platforms), and the web via both dart2js and dart2wasm.

Quick start #

import 'package:koni_archive/io.dart'; // VM/Flutter; web.dart for browsers

final archive = await openArchiveFile('volume01.cbz'); // format auto-detected

for (final entry in archive.glob('*.png')) {
  print('${entry.path}  ${entry.uncompressedSize} B');
}

// Streaming is the primary API: bounded memory for any entry size.
await for (final chunk in archive.openReadPath('page001.png')) {
  sink.add(chunk);
}

// Whole-entry convenience, with decompression-bomb protection.
final bytes = await archive.readBytes(entry, maxSize: 50 << 20);

await archive.close();

Password-protected archives (ZIP zipcrypto/AES, 7z AES-256 incl. encrypted headers, RAR5/RAR4) decrypt transparently — supply the password at open:

final archive = await openArchiveFile(
  'locked.cbz',
  options: const ArchiveReadOptions(password: 'hunter2'),
);

On the web, open a user-picked file without downloading it into memory:

import 'package:koni_archive/web.dart';

final archive = await openArchiveBlob(fileInput.files!.item(0)!);

Writing mirrors reading — pick a format, add entries, close:

final writer = await createArchiveFile(
  'volume01.cb7',
  format: const SevenZWriteFormat(), // or ZipWriteFormat, TarWriteFormat
);
await writer.addBytes(ArchiveEntrySpec(path: 'page001.png'), pngBytes);
await writer.close();

ZIP and 7z writers can encrypt with AES-256 — pass a password at create (WinZip AE-2 for ZIP, AES-256-CBC for 7z):

final writer = await createArchiveFile(
  'locked.cbz',
  format: const ZipWriteFormat(),
  options: const ArchiveWriteOptions(password: 'hunter2'),
);

Every writer's output is interop-verified against its reference tool (7zz, Info-ZIP unzip, bsdtar; encrypted output against 7zz) in CI-facing tests; 7z defaults to LZMA2 folders with compressed headers, and already-compressed content can opt into ArchiveCompression.stored per entry.

The virtual-filesystem model #

An Archive is a read-only virtual filesystem: you never care which format is underneath.

  • entries — the raw index, in archive order, duplicates included.
  • entry(path) / exists(path) — exact, case-sensitive lookup; duplicate paths resolve last-wins.
  • walk(), files, directories — the VFS view: one node per unique path, implicit parent directories synthesized (many ZIPs omit them), depth-first pre-order.
  • glob('ch01/*.webp') — pattern matching over the VFS view.

All entry paths are normalized and sanitized at parse time (/ separators, no drive letters, no .. escapes — attempts are flagged on the entry, never followed). Checksums are verified by default; symlinks are metadata only.

Concurrency and the isolate pattern (Flutter) #

Multiple entry streams may be open simultaneously — a reader preloads page N+1 while displaying page N:

final current = archive.readBytes(pages[n]);
final next = archive.readBytes(pages[n + 1]); // in flight concurrently

Decompression is CPU-bound. In Flutter apps, wrap whole-entry reads in Isolate.run so the UI thread never janks — entries are immutable and isolate-transferable by design:

final page = await Isolate.run(() async {
  final archive = await openArchiveFile(path);
  try {
    return await archive.readBytes(archive.entry(pagePath)!);
  } finally {
    await archive.close();
  }
});

(On the web, isolates don't exist; calls run inline — which is why nothing in this API requires them.)

Why not package:archive? #

package:archive is a fine general-purpose library; koni_archive exists for a different job — treating archives as random-access virtual filesystems for streaming consumption:

koni_archive package:archive
Read model random access via ByteSource (file, blob, memory; HTTP-range possible) whole archive bytes in memory (or VM-only file APIs)
Entry content single-subscription streams, bounded memory at any size whole entry buffers
Open cost metadata only — no content decode varies; content slices held per file
Checksums verified by default, typed error on mismatch not verified on read
Hostile input typed ArchiveException hierarchy, fuzzed in CI, path sanitization + escape flags assorted exceptions
Formats open registry — third-party formats plug in fixed set
Web dart2js and dart2wasm tested in CI dart2js
Writing TAR, ZIP, 7z (pure-Dart LZMA/LZMA2 encoder), AES encryption, interop-verified yes

If you are building a comic/ebook reader, file explorer, or anything that streams files out of archives, this is the library shaped for it.

Errors #

Everything archive-related throws a typed ArchiveException: UnsupportedFormatException, CorruptArchiveException, UnexpectedEofException, ChecksumMismatchException, EncryptedArchiveException (a password is required), InvalidPasswordException (the supplied password is wrong, where the format carries a check value), UnsupportedCompressionException (naming the method), and friends. Entry-scoped problems surface when reading that entry — one exotic entry never bricks the archive.

Status #

0.5.0 (git-only, lockstep releases) — reading, writing, and read-side decryption are complete. ZIP/CBZ (stored + deflate, ZIP64), TAR/CBT (ustar/PAX/GNU), GZIP (multi-member, layered .tar.gz), 7z/CB7 (LZMA/LZMA2/BCJ/delta, solid-block cache), and RAR/CBR (clean-room RAR5 + RAR4) read; TAR, ZIP, and 7z write; password-protected ZIP, 7z, and RAR archives decrypt via ArchiveReadOptions.password, and ZIP (WinZip AES-256) and 7z (AES-256) can be written encrypted via ArchiveWriteOptions.password. All of it is tested against reference tools, differential-tested against package:archive, fuzzed in CI, and verified on the VM, dart2js, and dart2wasm. What remains (RAR -hp headers, multi-volume, ZIP strong encryption, …) is tracked in ROADMAP.md at the repository root.

See example/ for a CBZ page extractor demonstrating streaming + preloading.

2
likes
0
points
217
downloads

Publisher

unverified uploader

Weekly Downloads

Format-agnostic archive reading and writing for Dart — one streaming-first API over ZIP, TAR, GZIP, 7z, and RAR. Facade of the koni_archive ecosystem.

Repository (GitHub)
View/report issues

Topics

#archive #zip #compression #comics

License

unknown (license)

Dependencies

glob, koni_archive_core, koni_gzip, koni_rar, koni_sevenz, koni_tar, koni_zip, path, web

More

Packages that depend on koni_archive