runlot

put, get, list

How to upload, download, and list files.

Upload

app.post("/upload", async (c) => {
  const form = await c.req.formData();
  const file = form.get("file") as File;
  const obj = await c.env.storage.put(`covers/${file.name}`, file, {
    contentType: file.type,
  });
  return Response.json({ key: obj.key, size: obj.size, etag: obj.etag });
});

body accepts anything you could use as a Response or Request body: File, Blob, ArrayBuffer, ReadableStream, and strings.

If you don't set contentType, a default is used. Set it explicitly when the browser needs to handle the file correctly.

Download

const obj = await env.storage.get(key);
if (!obj) return new Response("File not found", { status: 404 });

return new Response(obj.body, {
  headers: {
    "content-type": obj.contentType,
    "content-length": String(obj.size),
    etag: obj.etag,
  },
});

body is a stream, so you can pass it straight to the response without loading the whole file into memory.

If you only need the metadata, use head.

const meta = await env.storage.head(key);
if (meta && meta.etag === request.headers.get("if-none-match")) {
  return new Response(null, { status: 304 });
}

Delete

await env.storage.delete(key);

List

let cursor = "";
do {
  const page = await env.storage.list({ prefix: "covers/", cursor, limit: 100 });
  for (const obj of page.objects) {
    console.log(obj.key, obj.size);
  }
  cursor = page.cursor;
} while (cursor !== "");

An empty cursor means you have reached the last page. limit defaults to 100 and can be at most 1,000.

prefix is a string prefix, not a directory. Passing covers/, for example, returns every key that starts with that string.

Let the browser upload large files directly

Uploading through the worker consumes request time and memory. For large files, use a signed URL so the browser uploads directly.

On this page