[ Data & storage ]
@sdxc/file-storage-r2
A remix/file-storage FileStorage backed by a Cloudflare R2 bucket
- Used by
- blog
- Source
- packages/file-storage-r2
A remix/file-storage FileStorage backed by a
Cloudflare R2 bucket.
Installation
npm add @sdxc/file-storage-r2
It implements the FileStorage interface from remix,
which you install alongside it.
Usage
Create the storage
import { R2FileStorage } from "@sdxc/file-storage-r2";
let storage = new R2FileStorage(env.UPLOADS);
An R2Bucket binding passes straight through.
Store and read a file
await storage.set("avatars/ada.png", file);
let avatar = await storage.get("avatars/ada.png"); // File | null
avatar?.name; // "ada.png"
avatar?.type; // "image/png"
A file reads back as a native File with the name, type and lastModified it was stored
with. An object
written to the bucket some other way reads back named by its key, typed by its content
type, and dated by when R2 received it.
Page through a prefix
let cursor: string | undefined;
do {
let page = await storage.list({ prefix: "avatars/", limit: 100, cursor, includeMetadata: true });
for (let file of page.files) console.info(file.key, file.size, file.type);
cursor = page.cursor;
} while (cursor);
API
new R2FileStorage(bucket: R2FileBucket)
A FileStorage over the bucket. The file's name and lastModified are stored as the
object's custom metadata and its type as the content type.
get(key)resolves the storedFile, read whole into memory, ornullwhen the key holds nothing.has(key)answers with aHEAD, so it transfers no body.list(options?)lists keys in order.prefix,cursorandlimitpass to R2; the result carries acursorwhile more remain. WithincludeMetadata: trueeach entry also hasname,size,typeandlastModified.put(key, file)stores the file and resolves it as read back from the bucket.set(key, file)stores the file, replacing whatever the key held.remove(key)deletes the object; removing a missing key succeeds.
Types
R2FileBucket
The bucket methods the storage calls (get, head, list, put, delete). Every
R2Bucket binding satisfies it, whether wrangler types or @cloudflare/workers-types typed
it, so the package needs no Cloudflare type definitions of its own.
R2FileObject
An object as head and list answer it: key, size, uploaded and its metadata.
Pattern: Cache rendered images in R2
Render once per key, then serve the stored file on every later request.
import { R2FileStorage } from "@sdxc/file-storage-r2";
export default {
async fetch(request: Request, env: { IMAGES: R2Bucket }): Promise<Response> {
let storage = new R2FileStorage(env.IMAGES);
let key = new URL(request.url).pathname.slice(1);
let file = await storage.get(key);
if (!file) {
let bytes = await render(key);
file = await storage.put(key, new File([bytes], `${key}.png`, { type: "image/png" }));
}
return new Response(file, {
headers: { "Content-Type": file.type, "Cache-Control": "public, max-age=31536000" },
});
},
};