9.7 KiB
R2 Workers API Complete Reference
Last Updated: 2025-10-21 Official Docs: https://developers.cloudflare.com/r2/api/workers/workers-api-reference/
R2Bucket Methods
put()
Upload an object to R2.
put(
key: string,
value: ReadableStream | ArrayBuffer | ArrayBufferView | string | Blob,
options?: R2PutOptions
): Promise<R2Object | null>
Parameters:
key- Object key (path) in the bucketvalue- Object dataoptions- Optional upload options
Returns:
R2Object- Metadata of uploaded objectnull- If precondition failed (onlyIf clause)
Options (R2PutOptions):
interface R2PutOptions {
httpMetadata?: R2HTTPMetadata;
customMetadata?: Record<string, string>;
md5?: ArrayBuffer;
sha1?: ArrayBuffer;
sha256?: ArrayBuffer;
sha384?: ArrayBuffer;
sha512?: ArrayBuffer;
onlyIf?: R2Conditional;
storageClass?: 'Standard';
}
get()
Download an object from R2.
get(
key: string,
options?: R2GetOptions
): Promise<R2ObjectBody | null>
Parameters:
key- Object key (path) in the bucketoptions- Optional download options
Returns:
R2ObjectBody- Object with metadata and body streamnull- If object doesn't exist or precondition failed
Options (R2GetOptions):
interface R2GetOptions {
onlyIf?: R2Conditional | Headers;
range?: R2Range;
}
head()
Get object metadata without downloading body.
head(key: string): Promise<R2Object | null>
Parameters:
key- Object key (path) in the bucket
Returns:
R2Object- Object metadata onlynull- If object doesn't exist
Use Cases:
- Check if file exists
- Get file size
- Get last modified date
- Validate etag
delete()
Delete one or more objects.
delete(key: string | string[]): Promise<void>
Parameters:
key- Single key or array of keys (max 1000)
Returns:
void- Always succeeds (idempotent)
Notes:
- No error if object doesn't exist
- Can delete up to 1000 objects at once
- Deletes are strongly consistent
list()
List objects in the bucket.
list(options?: R2ListOptions): Promise<R2Objects>
Parameters:
options- Optional listing options
Returns:
R2Objects- List of objects and metadata
Options (R2ListOptions):
interface R2ListOptions {
limit?: number; // Max 1000, default 1000
prefix?: string; // Filter by prefix
cursor?: string; // Pagination cursor
delimiter?: string; // Folder delimiter (usually '/')
include?: ('httpMetadata' | 'customMetadata')[];
}
Response (R2Objects):
interface R2Objects {
objects: R2Object[]; // Array of objects
truncated: boolean; // true if more results exist
cursor?: string; // Cursor for next page
delimitedPrefixes: string[]; // "Folder" names (if delimiter used)
}
createMultipartUpload()
Create a new multipart upload.
createMultipartUpload(
key: string,
options?: R2MultipartOptions
): Promise<R2MultipartUpload>
Parameters:
key- Object key for the uploadoptions- Optional metadata
Returns:
R2MultipartUpload- Object for managing the upload
Options (R2MultipartOptions):
interface R2MultipartOptions {
httpMetadata?: R2HTTPMetadata;
customMetadata?: Record<string, string>;
}
resumeMultipartUpload()
Resume an existing multipart upload.
resumeMultipartUpload(
key: string,
uploadId: string
): R2MultipartUpload
Parameters:
key- Object key for the uploaduploadId- Upload ID from createMultipartUpload()
Returns:
R2MultipartUpload- Object for managing the upload
Notes:
- Does NOT validate uploadId or key
- No network request made
- Use to continue an upload after Worker restart
R2Object Interface
Metadata for an R2 object.
interface R2Object {
key: string; // Object key
version: string; // Version ID
size: number; // Size in bytes
etag: string; // ETag (without quotes)
httpEtag: string; // ETag with quotes (RFC 9110)
uploaded: Date; // Upload timestamp
httpMetadata?: R2HTTPMetadata; // HTTP metadata
customMetadata?: Record<string, string>; // Custom metadata
range?: R2Range; // Range (if partial)
checksums?: R2Checksums; // Checksums
storageClass: 'Standard'; // Storage class
ssecKeyMd5?: string; // SSE-C key hash
writeHttpMetadata(headers: Headers): void; // Apply metadata to headers
}
R2ObjectBody Interface
Extends R2Object with body stream and read methods.
interface R2ObjectBody extends R2Object {
body: ReadableStream; // Object body stream
bodyUsed: boolean; // Whether body consumed
arrayBuffer(): Promise<ArrayBuffer>; // Read as ArrayBuffer
text(): Promise<string>; // Read as text
json<T>(): Promise<T>; // Read as JSON
blob(): Promise<Blob>; // Read as Blob
}
R2MultipartUpload Interface
Manage a multipart upload.
interface R2MultipartUpload {
key: string; // Object key
uploadId: string; // Upload ID
uploadPart(
partNumber: number,
value: ReadableStream | ArrayBuffer | ArrayBufferView | string | Blob,
options?: R2MultipartOptions
): Promise<R2UploadedPart>;
abort(): Promise<void>;
complete(uploadedParts: R2UploadedPart[]): Promise<R2Object>;
}
Methods:
- uploadPart() - Upload a single part (1-10,000)
- abort() - Cancel the multipart upload
- complete() - Finish upload with list of parts
R2UploadedPart Interface
Metadata for an uploaded part.
interface R2UploadedPart {
partNumber: number; // Part number (1-10,000)
etag: string; // Part ETag
}
R2HTTPMetadata Interface
HTTP headers for object.
interface R2HTTPMetadata {
contentType?: string; // Content-Type header
contentLanguage?: string; // Content-Language header
contentDisposition?: string; // Content-Disposition header
contentEncoding?: string; // Content-Encoding header
cacheControl?: string; // Cache-Control header
cacheExpiry?: Date; // Expires header
}
R2Conditional Interface
Conditional operations (onlyIf clause).
interface R2Conditional {
etagMatches?: string; // If-Match
etagDoesNotMatch?: string; // If-None-Match
uploadedBefore?: Date; // If-Unmodified-Since
uploadedAfter?: Date; // If-Modified-Since
}
Alternatively, pass a Headers object with:
If-MatchIf-None-MatchIf-Modified-SinceIf-Unmodified-Since
R2Range Interface
Byte range for partial downloads.
interface R2Range {
offset?: number; // Start byte
length?: number; // Number of bytes
suffix?: number; // Last N bytes
}
Examples:
// First 1000 bytes
{ offset: 0, length: 1000 }
// Bytes 100-200
{ offset: 100, length: 100 }
// From byte 1000 to end
{ offset: 1000 }
// Last 500 bytes
{ suffix: 500 }
R2Checksums Interface
Stored checksums for object.
interface R2Checksums {
md5?: ArrayBuffer;
sha1?: ArrayBuffer;
sha256?: ArrayBuffer;
sha384?: ArrayBuffer;
sha512?: ArrayBuffer;
}
Complete Example
import { Hono } from 'hono';
type Bindings = {
MY_BUCKET: R2Bucket;
};
const app = new Hono<{ Bindings: Bindings }>();
// Upload with all metadata
app.put('/files/:key', async (c) => {
const key = c.req.param('key');
const body = await c.req.arrayBuffer();
const object = await c.env.MY_BUCKET.put(key, body, {
httpMetadata: {
contentType: c.req.header('content-type') || 'application/octet-stream',
cacheControl: 'public, max-age=3600',
contentDisposition: `attachment; filename="${key}"`,
},
customMetadata: {
uploadedBy: 'api',
uploadedAt: new Date().toISOString(),
},
onlyIf: {
// Only upload if file doesn't exist
uploadedBefore: new Date('2020-01-01'),
},
});
if (!object) {
return c.json({ error: 'File already exists' }, 409);
}
return c.json({
key: object.key,
size: object.size,
etag: object.etag,
});
});
// Download with range support
app.get('/files/:key', async (c) => {
const key = c.req.param('key');
const rangeHeader = c.req.header('range');
let options: R2GetOptions | undefined;
if (rangeHeader) {
// Parse range header: bytes=0-1000
const match = rangeHeader.match(/bytes=(\d+)-(\d*)/);
if (match) {
const start = parseInt(match[1]);
const end = match[2] ? parseInt(match[2]) : undefined;
options = {
range: {
offset: start,
length: end ? end - start + 1 : undefined,
},
};
}
}
const object = await c.env.MY_BUCKET.get(key, options);
if (!object) {
return c.json({ error: 'Not found' }, 404);
}
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set('etag', object.httpEtag);
if (object.range) {
headers.set('content-range', `bytes ${object.range.offset}-${object.range.offset + object.range.length - 1}/${object.size}`);
return new Response(object.body, {
status: 206,
headers,
});
}
return new Response(object.body, { headers });
});
export default app;