Ebee.Cloudflare.R2
3.0.0
Prefix Reserved
dotnet add package Ebee.Cloudflare.R2 --version 3.0.0
NuGet\Install-Package Ebee.Cloudflare.R2 -Version 3.0.0
<PackageReference Include="Ebee.Cloudflare.R2" Version="3.0.0" />
<PackageVersion Include="Ebee.Cloudflare.R2" Version="3.0.0" />
<PackageReference Include="Ebee.Cloudflare.R2" />
paket add Ebee.Cloudflare.R2 --version 3.0.0
#r "nuget: Ebee.Cloudflare.R2, 3.0.0"
#:package Ebee.Cloudflare.R2@3.0.0
#addin nuget:?package=Ebee.Cloudflare.R2&version=3.0.0
#tool nuget:?package=Ebee.Cloudflare.R2&version=3.0.0
Ebee.Cloudflare.R2
A comprehensive .NET client library for Cloudflare R2 storage, providing a simple and intuitive API for managing buckets, objects, signed URLs, and multipart uploads.
Features
- Bucket Management: Create, list, and delete R2 buckets
- Object Operations: Upload, download, copy, delete, and list objects
- Streaming Downloads: Stream large objects without buffering them in memory
- Metadata Support: Full support for custom metadata and headers
- Signed URLs: Generate pre-signed URLs for secure access
- Multipart Uploads: Handle large file uploads efficiently
- Encryption: Support for server-side encryption (SSE-S3, SSE-C)
- Async/Await: Full asynchronous support with cooperative cancellation
- Strongly Typed: Immutable, strongly-typed request/response models
- Error Handling: Detailed exception handling with R2-specific errors
- Dependency Injection: Built-in DI container support
Installation
dotnet add package Ebee.Cloudflare.R2
Quick Start
1. Configure Services
using Ebee.Cloudflare.R2;
services.AddR2Client(options =>
{
options.AccountId = "your-account-id";
options.AccessKeyId = "your-access-key-id";
options.SecretAccessKey = "your-secret-access-key";
});
⚠️ Never hard-code credentials in source. The literals above are for illustration only. See Configuration & Secrets for the recommended, config-driven setup.
2. Use the Client
using Ebee.Cloudflare.R2;
using Ebee.Cloudflare.R2.Objects.Models;
public class FileService
{
private readonly IR2Client _r2Client;
public FileService(IR2Client r2Client)
{
_r2Client = r2Client;
}
public async Task<string?> UploadFileAsync(string bucketName, string key, Stream content)
{
var request = new R2PutObjectRequest
{
BucketName = bucketName,
Key = key,
ContentStream = content, // the caller owns and disposes this stream
ContentType = "application/octet-stream"
};
var response = await _r2Client.PutObjectAsync(request);
return response.ETag;
}
}
Documentation
For detailed documentation and usage examples, please refer to the following topics:
- Buckets: Create, list, and delete R2 buckets — Documentation • Sample
- Objects: Upload, download, and manage objects — Documentation • Sample
- Signed URLs: Generate pre-signed URLs for secure access — Documentation • Sample
- Multipart Uploads: Handle large file uploads efficiently — Documentation • Sample
API Overview
IR2Client exposes operations two ways — pick whichever reads better in your code:
// 1. Flattened convenience methods directly on IR2Client:
await r2Client.PutObjectAsync(request);
await r2Client.GetObjectAsync(request);
await r2Client.GetObjectStreamAsync(request); // streaming download
r2Client.GenerateGetSignedUrl(request);
// 2. Grouped sub-clients, if you prefer to inject a narrower surface:
IBucketsClient Buckets; // Bucket operations
IObjectsClient Objects; // Object operations
ISignedUrlsClient SignedUrls; // Pre-signed URLs
IMultipartUploadsClient MultipartUploads; // Large file uploads
Both surfaces call the same underlying implementations. You can also inject any of the
grouped interfaces (IObjectsClient, etc.) directly instead of IR2Client.
Configuration & Secrets
Credentials are sensitive — keep them out of source control. The recommended approach is to
bind configuration from a secure source using the IConfiguration overload:
// Reads the "R2" section by default (override with a second argument).
services.AddR2Client(builder.Configuration);
services.AddR2Client(builder.Configuration, sectionName: "CloudflareR2");
Expected configuration shape (e.g. appsettings.json, user-secrets, or Key Vault):
{
"R2": {
"AccountId": "your-account-id",
"AccessKeyId": "your-access-key-id",
"SecretAccessKey": "your-secret-access-key"
}
}
- Local development: use user secrets
(
dotnet user-secrets set "R2:SecretAccessKey" "..."). - Production: use a secret store such as Azure Key Vault / AWS Secrets Manager, or
environment variables mapped through configuration (the .NET environment-variable provider
binds
R2__AccountId,R2__AccessKeyId,R2__SecretAccessKeyusing the__separator).
Configuration is validated at registration time: if AccountId, AccessKeyId, or
SecretAccessKey is missing, AddR2Client throws an InvalidOperationException immediately
rather than failing later on the first request.
Additional options
| Option | Default | Description |
|---|---|---|
EndpointUrl |
https://{AccountId}.r2.cloudflarestorage.com |
Override the R2 endpoint. |
DisablePayloadSigning |
true |
Required for streaming uploads to R2. |
DisableChecksumValidation |
true |
Disables the SDK's default upload checksum. See the security note below. |
🔒 Upload integrity note. For Cloudflare R2 compatibility this client, by default, disables AWS request payload signing and the SDK's default checksum validation on
PutObject/UploadPart. This means uploads are not protected by an end-to-end SDK checksum. If your R2 setup supports it, you can opt back into checksum validation by settingoptions.DisableChecksumValidation = false. You can always supply your ownContentMD5on multipart parts for integrity.
Dependency Injection & Thread Safety
AddR2Clientregisters the underlyingIAmazonS3client as a singleton and the R2 clients (IR2Clientand the grouped interfaces) as scoped.- The clients are stateless wrappers over the thread-safe
IAmazonS3, so they are safe to use concurrently. ResolveIR2Clientfrom DI rather than constructing it manually.
Streaming Downloads & Disposal
GetObjectAsync buffers the whole object in memory (convenient for small payloads). For large
objects, use GetObjectStreamAsync, which hands you the live network stream and never buffers
the full payload:
using Ebee.Cloudflare.R2.Objects.Models;
var request = new R2GetObjectRequest { BucketName = "my-bucket", Key = "large-file.zip" };
await using var response = await r2Client.GetObjectStreamAsync(request);
await response.ContentStream.CopyToAsync(destinationStream);
R2GetObjectStreamResponseandR2GetObjectResponseareIDisposable— dispose them (using/await using) to release the underlying stream and HTTP connection.- Request objects do not own streams you assign to
ContentStream; you are responsible for disposing any stream you supply.
Error Handling
All operations may throw R2Exception with detailed error information. Cancellation is
cooperative — a cancelled CancellationToken surfaces as OperationCanceledException, not
R2Exception:
try
{
var response = await _r2Client.GetObjectAsync(request, cancellationToken);
}
catch (OperationCanceledException)
{
// the operation was cancelled
}
catch (R2Exception ex)
{
Console.WriteLine($"R2 operation failed: {ex.Message}");
// Check inner exception for AWS S3 specific details
if (ex.InnerException is AmazonS3Exception s3Ex)
{
Console.WriteLine($"S3 Error Code: {s3Ex.ErrorCode}");
}
}
Getting R2 Credentials
- Log in to your Cloudflare Dashboard
- Navigate to R2 Object Storage
- Go to Manage R2 API tokens
- Create a new API token with appropriate permissions
- Note your Account ID from the R2 dashboard
Examples Repository
Check out the samples/ directory for complete working examples:
- Bucket Management - Create, list, delete buckets
- Object Operations - Upload, download, copy, delete objects
- Signed URLs - Generate pre-signed URLs
- Multipart Uploads - Handle large file uploads
Requirements
- .NET 6.0, .NET 8.0, .NET 9.0, or .NET 10.0
- Valid Cloudflare R2 credentials
Contributing
We welcome contributions! Please see our Contributing Guidelines for details.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
Happy coding with Cloudflare R2!
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- AWSSDK.S3 (>= 4.0.17)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Http (>= 10.0.0)
-
net6.0
- AWSSDK.S3 (>= 4.0.17)
- Microsoft.Extensions.Configuration.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 6.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Http (>= 6.0.0)
-
net8.0
- AWSSDK.S3 (>= 4.0.17)
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Http (>= 8.0.0)
-
net9.0
- AWSSDK.S3 (>= 4.0.17)
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Http (>= 9.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v3.0.0: Added streaming downloads (GetObjectStreamAsync); configurable upload integrity (payload signing / checksum validation); startup options validation; cancellation now propagates as OperationCanceledException. Breaking: response models are now init-only immutable, request models use the 'required' keyword, and content-carrying request types no longer implement IDisposable (callers own their streams). See CHANGELOG.md.