Skip to content

Instantly share code, notes, and snippets.

@w00kie
Last active September 16, 2024 03:42
Show Gist options
  • Save w00kie/09154981be8af8e757ed240c6844b2fc to your computer and use it in GitHub Desktop.
Save w00kie/09154981be8af8e757ed240c6844b2fc to your computer and use it in GitHub Desktop.
Cloudflare Worker that bundles R2 objects in a Zip file
import { z } from 'zod';
import { ZipWriter, BlobReader, configure } from '@zip.js/zip.js';
// Without this, we get uncaught error due to Workers runtime bug
// See: https://github.com/gildas-lormeau/zip.js/discussions/514
configure({
useCompressionStream: false,
});
// Payload schema that lists the files to be bundled, their filenames and the archive filename
const schema = z.object({
archive_filename: z.string().endsWith('.zip'),
files: z.array(
z.object({
r2key: z.string(),
filename: z.string(),
})
),
});
export default {
async fetch(request, env, ctx): Promise<Response> {
const body = await request.json();
const payload = schema.safeParse(body);
if (!payload.success) {
return new Response(JSON.stringify({ status: 'failed', error: payload.error }), {
status: 409,
headers: { 'Content-Type': 'application/json' },
});
}
let { readable, writable } = new IdentityTransformStream();
// Create a ZIP archive stream
const archive = new ZipWriter(writable);
// Store all the promises to catch any errors all together later
let promises: Promise<any>[] = [];
for (const receipt of payload.data.files) {
const { r2key, filename } = receipt;
// Fetch the receipt from the R2 bucket
const fileContent = await env.STORAGE_BUCKET.get(r2key);
if (!fileContent) {
return new Response(`Object not found: ${r2key}`, { status: 404 });
}
// Add the receipt to the ZIP archive
promises.push(archive.add(filename, new BlobReader(await fileContent.blob())));
}
promises.push(archive.close());
Promise.all(promises).catch((err) => {
console.log(err);
});
return new Response(readable, {
headers: {
'Content-Type': 'application/zip',
'Content-Disposition': `attachment; filename="${payload.data.archive_filename}"`,
},
});
},
} satisfies ExportedHandler<Env>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment