Skip to content

streaming Zip file creation #2237

Description

@Touffy

I am the developer of client-zip, a very small, pretty fast streaming Zip file generator, written in modern JavaScript and a bit of WASM. A user suggested to me — insistently — that client-zip (or something based on it) should become part of the Deno standard library, presumably alongside the existing archive/tar module. client-zip already runs very well in Deno, by the way.

Why not ?

This is not rhetorical. I actually don't think it's such a good idea, and I'm posting here to explain and see what happens.

The main reason for creating Zip archives instead of the more elegant Tar+Gzip format is because Zip enjoys universal support out of the box. I think it makes more sense to do the work client-side, but maybe I'm not seeing some good use cases.

client-zip has a very different design compared to what's in the Go library and most existing libraries. Instead of instantiating an object to represent the archive and calling methods on it to add files, client-zip exposes a single function that takes an async iterable of inputs and immediately returns an output stream (which is generated lazily from that moment on).

I like my design choice very much, and I think it meshes well with core Deno code which also favors async generators — particularly the wonderful fs/walk module. But it's a big side-step from the guideline of sticking with the Go stdlib, and the interface of the existing tar module. It would look like this if you wanted to zip a directory :

import { walk } from "https://deno.land/std@0.139.0/fs/walk.ts"
import { downloadZip } from "https://unpkg.com/client-zip/index.js"

async function *readFiles() {
  for await (const entry of walk(".", { includeDirs: false }))
    yield await Deno.open(entry.path)
}

// currently this would be a Response, but we could return the ReadableStream directly
const output = downloadZip(readFiles())

client-zip is designed around streaming and therefore never looking ahead at file data. That means, in general (and particularly if you use compression, when that's implemented), if you create a Zip stream to feed an HTTP Response, you won't be able to send a Content-Length with that Response. The upside, of course, is low latency, low memory usage, and no need to write temporary files.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions