function parseRange( encoded: string | null ): undefined | { offset: number; length: number } { if (encoded === null) { return; } const parts = encoded.split("bytes=")[1]?.split("-") ?? []; if (parts.length !== 2) { throw new Error( "Not supported to skip specifying the beginning/ending byte at this time" ); } return { offset: Number(parts[0]), length: Number(parts[1]) + 1 - Number(parts[0]), }; } function objectNotFound(objectName: string): Response { return new Response( `R2 object "${objectName}" not found`, { status: 404, headers: { "content-type": "text/html; charset=UTF-8", }, } ); } export default { async fetch(request: Request, env): Promise { const url = new URL(request.url); const objectName = url.pathname.slice(1); console.log(`${request.method} object ${objectName}: ${request.url}`); if (request.method === "GET" || request.method === "HEAD") { if (objectName === "") { if (request.method == "HEAD") { return new Response(undefined, { status: 400 }); } const options: R2ListOptions = { prefix: url.searchParams.get("prefix") ?? undefined, delimiter: url.searchParams.get("delimiter") ?? undefined, cursor: url.searchParams.get("cursor") ?? undefined, include: ["customMetadata", "httpMetadata"], }; console.log(JSON.stringify(options)); const listing = await env.R2_STORE.list(options); return new Response(JSON.stringify(listing), { headers: { "content-type": "application/json; charset=UTF-8", }, }); } if (request.method === "GET") { const range = parseRange(request.headers.get("range")); const object = await env.R2_STORE.get(objectName, { range, onlyIf: request.headers, }); if (object === null) { return objectNotFound(objectName); } const headers = new Headers(); object.writeHttpMetadata(headers); headers.set("etag", object.httpEtag); const status = object.body ? (range ? 206 : 200) : 304; return new Response(object.body, { headers, status, }); } const object = await env.R2_STORE.head(objectName); if (object === null) { return objectNotFound(objectName); } const headers = new Headers(); object.writeHttpMetadata(headers); headers.set("etag", object.httpEtag); return new Response(null, { headers, }); } /** if (request.method === 'PUT' || request.method == 'POST') { const object = await env.R2_STORE.put(objectName, request.body, { httpMetadata: request.headers, }) return new Response(null, { headers: { 'etag': object.httpEtag, } }) } if (request.method === 'DELETE') { await env.R2_STORE.delete(url.pathname.slice(1)) return new Response() } */ return new Response(`Unsupported method`, { status: 400, }); }, };