Skip to content
Built with

Object storage ​

Build & Deploy

Store files in Cloudflare R2 with storage from void/storage. You get the typed R2 API for uploads, downloads and metadata.

On this pageUpload, download and delete files · Serve files to visitors · Work with metadata

Basic operations ​

These examples upload, read, list and delete objects.

ts
import { storage } from 'void/storage';

// Upload
await storage.put('uploads/photo.jpg', file, {
  httpMetadata: { contentType: 'image/jpeg' },
});

// Download
const object = await storage.get('uploads/photo.jpg');
if (object) {
  const data = await object.arrayBuffer();
}

// Delete
await storage.delete('uploads/photo.jpg');

// List
const listed = await storage.list({ prefix: 'uploads/' });
for (const obj of listed.objects) {
  console.log(obj.key, obj.size);
}

// Head (metadata only)
const head = await storage.head('uploads/photo.jpg');

storage supports the full R2Bucket API.

Serving files ​

This step serves uploaded files from an API route.

ts
// routes/files/[key].ts
import { defineHandler } from 'void';
import { storage } from 'void/storage';

export const GET = defineHandler(async (c) => {
  const object = await storage.get(`uploads/${c.req.param('key')}`);

  if (!object) {
    return c.notFound();
  }

  const headers = new Headers();
  object.writeHttpMetadata(headers);
  headers.set('etag', object.httpEtag);
  // Don't let an uploaded HTML or SVG file run as a page on your site.
  headers.set('X-Content-Type-Options', 'nosniff');
  headers.set('Content-Disposition', 'attachment');

  return new Response(object.body, { headers });
});

Files your users upload can contain HTML or scripts. Store them under keys your server picks, and serve them with nosniff and Content-Disposition: attachment. <img> and <video> tags still display them. Opening the URL directly downloads the file.

Custom bindings ​

This step wraps your own R2 binding with createStorage(bucket). It also works in tests.

ts
import { createStorage } from 'void/storage';

const storage = createStorage(env.MY_BUCKET);

Next steps ​

implemented.stream AI