Files
gh-jezweb-claude-skills-ski…/references/workers-api.md
2025-11-30 08:24:29 +08:00

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 bucket
  • value - Object data
  • options - Optional upload options

Returns:

  • R2Object - Metadata of uploaded object
  • null - 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 bucket
  • options - Optional download options

Returns:

  • R2ObjectBody - Object with metadata and body stream
  • null - 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 only
  • null - 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 upload
  • options - 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 upload
  • uploadId - 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-Match
  • If-None-Match
  • If-Modified-Since
  • If-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;