Table of Contents

R2 Object Storage

The Cloudflare.NET.R2 package provides a high-level S3-compatible client for Cloudflare R2 object storage with intelligent upload strategies, multipart support, presigned URLs, and comprehensive operation metrics.

Installation

dotnet add package Cloudflare.NET.R2

Quick Start

Configuration

Add R2 settings to your appsettings.json:

{
  "Cloudflare": {
    "AccountId": "your-account-id"
  },
  "R2": {
    "AccessKeyId": "your-r2-access-key-id",
    "SecretAccessKey": "your-r2-secret-access-key"
  }
}

Registration

// Program.cs
builder.Services.AddCloudflareR2Client(builder.Configuration);

Basic Usage

Inject IR2Client into your services:

public class StorageService(IR2Client r2)
{
    public async Task<R2Result> UploadFileAsync(string key, string filePath)
    {
        return await r2.UploadAsync("my-bucket", key, filePath);
    }

    public async Task<R2Result> DownloadFileAsync(string key, string downloadPath)
    {
        return await r2.DownloadFileAsync("my-bucket", key, downloadPath);
    }
}

Features

Feature Description
Intelligent Uploads Auto-selects single-part or multipart based on file size
Multipart Uploads Handle files up to 5 TiB with parallel part uploads
Presigned URLs Generate secure URLs for direct browser uploads
Jurisdiction Support EU, FedRAMP, US, and default global endpoints
Named Clients Multi-account scenarios with IR2ClientFactory
Operation Metrics Track Class A/B operations, ingress, and egress bytes
Error Handling Rich exceptions with partial metrics for debugging

Operation Pricing

R2 has a unique pricing model with free and paid operations:

Operation Type Examples Cost
Class A Put, List, Copy $4.50 per million
Class B Get, Head $0.36 per million
Free Delete Free
Egress Downloads Free

The SDK tracks these metrics in the R2Result object returned by all operations.

API Overview

Upload Operations

Method Description
UploadAsync Auto-select upload strategy, with optional content type, checksum and Cache-Control
UploadSinglePartAsync Force single PUT request, with optional content type, checksum and Cache-Control
UploadMultipartAsync Force multipart upload, with optional content type and Cache-Control

Download Operations

Method Description
DownloadFileAsync Download to file or stream

Delete Operations

Method Description
DeleteObjectAsync Delete single object (free)
DeleteObjectsAsync Batch delete (free)
ClearBucketAsync Delete all objects, then abort any upload left open

List Operations

Method Description
ListObjectsAsync List every object, walking all pages
ListObjectsPageAsync List one page and return its continuation token
ListPartsAsync List the parts of one multipart upload
ListMultipartUploadsAsync Find uploads started but never completed

Presigned URLs

Method Description
CreatePresignedGetUrl URL for direct download
CreatePresignedPutUrl URL for direct upload
CreatePresignedUploadPartUrl URL for multipart part
CreatePresignedUploadPartsUrls Batch URLs for parts

The upload URL methods can bind the upload to a digest of the expected bytes, which R2 verifies against what actually arrives: see Checksum Verification.

Low-Level Multipart

Method Description
InitiateMultipartUploadAsync Start multipart upload, optionally naming the finished object's content type and Cache-Control
CompleteMultipartUploadAsync Finalize multipart upload
AbortMultipartUploadAsync Cancel multipart upload

R2Result Model

All operations return R2Result with operation metrics:

public record R2Result(
    long ClassAOperations = 0,  // Writes, lists
    long ClassBOperations = 0,  // Reads
    long IngressBytes = 0,      // Bytes uploaded
    long EgressBytes = 0        // Bytes downloaded
);

Results can be combined:

var total = result1 + result2 + result3;
Console.WriteLine($"Total ingress: {total.IngressBytes} bytes");

Exception Handling

The SDK provides rich exceptions for error handling:

Exception Scenario
CloudflareR2OperationException Single operation failure
CloudflareR2BatchException<T> Batch operation with failures
CloudflareR2ListException<T> List operation failed mid-stream
try
{
    await r2.UploadAsync("bucket", "key", filePath);
}
catch (CloudflareR2OperationException ex)
{
    // Single operation failed
    Console.WriteLine($"Upload failed: {ex.Message}");
    Console.WriteLine($"Partial metrics: {ex.PartialMetrics}");
}
catch (CloudflareR2BatchException<string> ex)
{
    // Batch operation had failures
    Console.WriteLine($"Failed items: {ex.FailedItems.Count}");
    Console.WriteLine($"Partial metrics: {ex.PartialMetrics}");
}
catch (CloudflareR2ListException<S3Object> ex)
{
    // List operation failed mid-stream
    Console.WriteLine($"Partial data retrieved: {ex.PartialData.Count}");
}

Size Limits

Limit Value
Maximum object size 5 TiB
Single PUT limit 5 GiB
Minimum part size 5 MiB
Maximum part size 5 GiB
Maximum parts per upload 10,000

Required Permissions

Permission Description
s3:GetObject Download objects
s3:PutObject Upload objects
s3:DeleteObject Delete objects
s3:ListBucket List objects

Jurisdictions

R2 supports jurisdictional restrictions for data residency compliance. Use IR2ClientFactory to create clients for different jurisdictions:

public class MultiRegionService(IR2ClientFactory factory)
{
    public async Task ReplicateToEuAsync(string bucket, string key, Stream data)
    {
        // Create a client for the EU jurisdiction
        var euClient = factory.GetClient(R2Jurisdiction.EuropeanUnion);
        await euClient.UploadAsync(bucket, key, data);
    }
}
Jurisdiction Endpoint
Default https://{account_id}.r2.cloudflarestorage.com
EuropeanUnion https://{account_id}.eu.r2.cloudflarestorage.com
FedRamp https://{account_id}.fedramp.r2.cloudflarestorage.com
UnitedStates https://{account_id}.us.r2.cloudflarestorage.com

See R2 Object Storage for detailed jurisdiction documentation.