跳到主要内容

IGeekFan.R2.NET

Cloudflare R2 对象存储 .NET 客户端,基于 AWS S3 SDK。

功能概览

功能说明
文件上传上传文件到 R2 存储
文件下载按需下载文件
文件删除删除指定文件
S3 兼容兼容 AWS S3 协议

安装

dotnet add package IGeekFan.R2.NET

快速开始

1. 配置

{
"CloudflareR2": {
"ApiBaseUri": "https://<account-id>.r2.cloudflarestorage.com",
"AccessKeyId": "<your-access-key-id>",
"Secret": "<your-secret-access-key>",
"Host": "https://your-domain.com",
"Bucket": "your-bucket"
}
}

2. 注册服务

using IGeekFan.R2.NET;
using IGeekFan.R2.NET.Configuration;

builder.Services.Configure<CloudflareR2Options>(
builder.Configuration.GetSection(CloudflareR2Options.SettingsName));

builder.Services.AddSingleton<ICloudflareR2ClientFactory, CloudflareR2ClientFactory>();

3. 上传文件

public class FileService
{
private readonly ICloudflareR2ClientFactory _factory;

public FileService(ICloudflareR2ClientFactory factory) => _factory = factory;

public async Task<string> UploadAsync(Stream file, string fileName)
{
ICloudflareR2Client client = _factory.GetClient("my-bucket");
string url = await client.UploadBlobAsync(file, fileName, CancellationToken.None);
return url;
}

public async Task<Stream> DownloadAsync(string fileName)
{
ICloudflareR2Client client = _factory.GetClient("my-bucket");
return await client.GetBlobAsync(fileName, CancellationToken.None);
}

public async Task DeleteAsync(string fileName)
{
ICloudflareR2Client client = _factory.GetClient("my-bucket");
await client.DeleteBlobAsync(fileName, CancellationToken.None);
}
}

API 方法

方法说明参数
UploadBlobAsync上传文件Stream, string fileName, CancellationToken
GetBlobAsync下载文件string fileName, CancellationToken
DeleteBlobAsync删除文件string fileName, CancellationToken
ListBucketsAsync列出 BucketCancellationToken
ListObjectsAsync列出对象string prefix, CancellationToken
CopyObjectAsync复制对象string sourceKey, string destinationKey, CancellationToken

使用示例

上传图片

public async Task<string> UploadImageAsync(IFormFile file)
{
using var stream = file.OpenReadStream();
var fileName = $"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}";

var url = await _factory.GetClient("images")
.UploadBlobAsync(stream, fileName, CancellationToken.None);

return url;
}

批量上传

public async Task<List<string>> UploadFilesAsync(List<IFormFile> files)
{
var urls = new List<string>();
var client = _factory.GetClient("uploads");

foreach (var file in files)
{
using var stream = file.OpenReadStream();
var fileName = $"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}";
var url = await client.UploadBlobAsync(stream, fileName, CancellationToken.None);
urls.Add(url);
}

return urls;
}

生成签名 URL

public string GetSignedUrl(string fileName, int expirationMinutes = 60)
{
var client = _factory.GetClient("my-bucket");
// 生成带签名的临时访问 URL
return client.GetSignedUrl(fileName, expirationMinutes);
}

列出文件

public async Task<List<string>> ListFilesAsync(string prefix = "")
{
var client = _factory.GetClient("my-bucket");
var objects = await client.ListObjectsAsync(prefix, CancellationToken.None);
return objects.Select(o => o.Key).ToList();
}

配置选项

{
"CloudflareR2": {
"ApiBaseUri": "https://<account-id>.r2.cloudflarestorage.com",
"AccessKeyId": "<your-access-key-id>",
"Secret": "<your-secret-access-key>",
"Host": "https://your-domain.com",
"Bucket": "your-bucket"
}
}
选项说明必填
ApiBaseUriR2 API 地址
AccessKeyId访问密钥 ID
Secret访问密钥
Host自定义域名
Bucket默认 Bucket

获取 API Token

  1. 登录 Cloudflare Dashboard → R2 → Manage R2 API Tokens
  2. 创建 Token,选择 Admin Read & Write
  3. 记录 Access Key ID 和 Secret

与 FreeSql 集成

// 在实体中存储文件路径
public class Article : FullAuditEntity<Guid, Guid>
{
public string Title { get; set; } = default!;
public string CoverImage { get; set; } = default!; // R2 URL
}

// 上传并保存
public async Task UpdateCoverImageAsync(Guid articleId, IFormFile file)
{
var article = await _repository.GetByIdAsync(articleId);

using var stream = file.OpenReadStream();
var fileName = $"{articleId}/cover{Path.GetExtension(file.FileName)}";
article.CoverImage = await _r2Client.UploadBlobAsync(stream, fileName);

await _repository.UpdateAsync(article);
}

运维建议

建议说明
配置 CDN使用 Cloudflare CDN 加速访问
设置缓存静态资源设置长缓存
监控用量监控存储用量和请求量
定期清理清理无用文件

故障排查

问题解决方案
上传失败检查 AccessKeyId 和 Secret
403 Forbidden检查 Token 权限
文件不存在检查 Bucket 名称
超时检查网络连接

相关文档