Cloudflare R2 native bindings adapter for @arraypress/storage. Uses the R2Bucket API directly for zero SDK overhead and maximum performance on Cloudflare Workers.
Works in Cloudflare Workers only (requires R2 bucket binding).
npm install @arraypress/storage-r2 @arraypress/storageimport { Hono } from 'hono';
import { createR2Storage } from '@arraypress/storage-r2';
type Env = {
Bindings: {
BUCKET: R2Bucket;
};
};
const app = new Hono<Env>();
app.post('/upload', async (c) => {
const storage = createR2Storage({
bucket: c.env.BUCKET,
publicUrl: 'https://media.mystore.com',
});
const body = await c.req.arrayBuffer();
const result = await storage.upload({
key: 'uploads/photo.jpg',
body: new Uint8Array(body),
contentType: 'image/jpeg',
});
return c.json(result);
});
app.get('/download/:key{.+}', async (c) => {
const storage = createR2Storage({ bucket: c.env.BUCKET });
const file = await storage.download(c.req.param('key'));
if (!file) return c.notFound();
return new Response(file.body, {
headers: {
'Content-Type': file.contentType,
'Content-Length': String(file.size),
},
});
});
export default app;[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-bucket"import { createR2Storage } from '@arraypress/storage-r2';
import { contentHash, contentAddressedKey } from '@arraypress/storage';
const storage = createR2Storage({ bucket: env.BUCKET });
const hash = await contentHash(fileBuffer);
const key = contentAddressedKey(hash, 'photo.jpg', 'media/');
if (!await storage.exists(key)) {
await storage.upload({ key, body: fileBuffer, contentType: 'image/jpeg' });
}const storage = createR2Storage({ bucket: env.BUCKET });
const mpu = await storage.createMultipartUpload('large-file.zip', {
contentType: 'application/zip',
});
const part1 = await mpu.uploadPart(1, chunk1);
const part2 = await mpu.uploadPart(2, chunk2);
await mpu.complete([part1, part2]);R2 native bindings have limited presigned URL support. If you need reliable signed URLs (e.g. for direct client uploads), use @arraypress/storage-s3 with R2's S3-compatible API instead.
// May throw StorageError with code 'NOT_SUPPORTED' depending on environment
const signed = await storage.getSignedDownloadUrl({
key: 'uploads/photo.jpg',
expiresIn: 3600,
});Creates a Storage adapter backed by Cloudflare R2 native bindings.
| Option | Type | Required | Description |
|---|---|---|---|
bucket |
R2Bucket |
Yes | R2 bucket binding from wrangler.toml |
publicUrl |
string |
No | Public URL prefix for getPublicUrl() (e.g. 'https://media.mystore.com') |
StorageinterfaceStorageErrorclasscontentHash(),contentAddressedKey(),safeDisposition()helpers- All type definitions
MIT