名称: cloudflare-r2 描述: 实现Cloudflare R2的指南 - 具有零出口费用的S3兼容对象存储。当实施文件存储、上传/下载、数据迁移至/从R2、配置存储桶、与Workers集成或使用R2 API和SDK时使用。
Cloudflare R2
具有零出口带宽费用的S3兼容对象存储。基于Cloudflare的全球网络构建,具有高耐久性(11个9)和强一致性。
何时使用此技能
- 为应用程序实现对象存储
- 从AWS S3或其他存储提供商迁移
- 设置文件上传/下载
- 配置公共或私有存储桶
- 将R2与Cloudflare Workers集成
- 使用S3兼容工具和SDK与R2
- 配置CORS、生命周期或事件通知
- 通过零出口费用优化存储成本
先决条件
必需:
- 购买R2的Cloudflare账户
- 从Cloudflare仪表板获取账户ID
对于API访问:
- R2访问密钥(访问密钥ID + 秘密访问密钥)
- 从Cloudflare仪表板生成:R2 → 管理R2 API令牌
对于Wrangler CLI:
npm install -g wrangler
wrangler login
核心概念
架构
- S3兼容API - 与AWS SDK和工具兼容
- Workers API - 原生Cloudflare Workers集成
- 全球网络 - 所有区域的强一致性
- 零出口费用 - 数据检索无带宽费用
存储类别
- 标准 - 默认,针对频繁访问优化
- 不频繁访问 - 存储成本较低,收取检索费用,30天最低保留期
访问方法
- R2 Workers绑定 - 无服务器集成(推荐新应用)
- S3 API - 与现有工具兼容
- 公共存储桶 - 通过自定义域名或r2.dev直接HTTP访问
- 预签名URL - 无凭证临时访问
快速开始
1. 创建存储桶
Wrangler:
wrangler r2 bucket create my-bucket
带位置提示:
wrangler r2 bucket create my-bucket --location=wnam
位置:wnam(北美西部)、enam(北美东部)、weur(欧洲西部)、eeur(欧洲东部)、apac(亚太)
2. 上传对象
Wrangler:
wrangler r2 object put my-bucket/file.txt --file=./local-file.txt
Workers API:
await env.MY_BUCKET.put('file.txt', fileContents, {
httpMetadata: {
contentType: 'text/plain',
},
});
3. 下载对象
Wrangler:
wrangler r2 object get my-bucket/file.txt --file=./downloaded.txt
Workers API:
const object = await env.MY_BUCKET.get('file.txt');
const contents = await object.text();
Workers集成
绑定配置
wrangler.toml:
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"
preview_bucket_name = "my-bucket-preview"
常见操作
带元数据上传:
await env.MY_BUCKET.put('user-uploads/photo.jpg', imageData, {
httpMetadata: {
contentType: 'image/jpeg',
cacheControl: 'public, max-age=31536000',
},
customMetadata: {
uploadedBy: userId,
uploadDate: new Date().toISOString(),
},
});
流式下载:
const object = await env.MY_BUCKET.get('large-file.mp4');
if (object === null) {
return new Response('Not found', { status: 404 });
}
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata.contentType,
'ETag': object.etag,
},
});
列出对象:
const listed = await env.MY_BUCKET.list({
prefix: 'user-uploads/',
limit: 100,
});
for (const object of listed.objects) {
console.log(object.key, object.size);
}
删除对象:
await env.MY_BUCKET.delete('old-file.txt');
检查对象是否存在:
const object = await env.MY_BUCKET.head('file.txt');
if (object) {
console.log('Exists:', object.size, 'bytes');
}
S3 SDK集成
AWS CLI
配置:
aws configure
# 访问密钥 ID: <您的密钥ID>
# 秘密访问密钥: <您的秘密>
# 区域: auto
操作:
# 列出存储桶
aws s3api list-buckets --endpoint-url https://<账户ID>.r2.cloudflarestorage.com
# 上传文件
aws s3 cp file.txt s3://my-bucket/ --endpoint-url https://<账户ID>.r2.cloudflarestorage.com
# 生成预签名URL(1小时后过期)
aws s3 presign s3://my-bucket/file.txt --endpoint-url https://<账户ID>.r2.cloudflarestorage.com --expires-in 3600
JavaScript(AWS SDK v3)
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({
region: "auto",
endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
},
});
await s3.send(new PutObjectCommand({
Bucket: "my-bucket",
Key: "file.txt",
Body: fileContents,
}));
Python(Boto3)
import boto3
s3 = boto3.client(
service_name="s3",
endpoint_url=f'https://{account_id}.r2.cloudflarestorage.com',
aws_access_key_id=access_key_id,
aws_secret_access_key=secret_access_key,
region_name="auto",
)
# 上传文件
s3.upload_fileobj(file_obj, 'my-bucket', 'file.txt')
# 下载文件
s3.download_file('my-bucket', 'file.txt', './local-file.txt')
Rclone(大文件)
配置:
rclone config
# 选择: Amazon S3 → Cloudflare R2
# 输入凭证和端点
带多部分优化上传:
# 对于大文件(>100MB)
rclone copy large-video.mp4 r2:my-bucket/ \
--s3-upload-cutoff=100M \
--s3-chunk-size=100M
公共存储桶
启用公共访问
Wrangler:
wrangler r2 bucket create my-public-bucket
# 然后在仪表板启用: R2 → 存储桶 → 设置 → 公共访问
访问URL
r2.dev(仅开发,速率限制):
https://pub-<哈希>.r2.dev/file.txt
自定义域名(推荐生产):
- 仪表板 → R2 → 存储桶 → 设置 → 公共访问
- 添加自定义域名
- Cloudflare自动处理DNS/TLS
CORS配置
需要用于:
- 基于浏览器的上传
- 跨源API调用
- 从Web应用使用预签名URL
Wrangler:
wrangler r2 bucket cors put my-bucket --rules '[
{
"AllowedOrigins": ["https://example.com"],
"AllowedMethods": ["GET", "PUT", "POST"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3600
}
]'
重要: 源必须精确匹配(无尾随斜杠)。
多部分上传
对于文件 >100MB 或并行上传:
Workers API:
const multipart = await env.MY_BUCKET.createMultipartUpload('large-file.mp4');
// 上传部分(每个5MiB - 5GiB,最多10,000部分)
const part1 = await multipart.uploadPart(1, chunk1);
const part2 = await multipart.uploadPart(2, chunk2);
// 完成上传
const object = await multipart.complete([part1, part2]);
约束:
- 部分大小:5MiB - 5GiB
- 最大部分数:10,000
- 最大对象大小:5TB
- 不完整上传在7天后自动中止(可通过生命周期配置)
数据迁移
Sippy(增量、按需)
最佳用于:渐进迁移,避免前期出口费用
# 为存储桶启用
wrangler r2 bucket sippy enable my-bucket \
--provider=aws \
--bucket=source-bucket \
--region=us-east-1 \
--access-key-id=$AWS_KEY \
--secret-access-key=$AWS_SECRET
对象首次请求时迁移。后续请求从R2提供。
Super Slurper(批量、一次性)
最佳用于:完整迁移,已知对象列表
- 仪表板 → R2 → 数据迁移 → Super Slurper
- 选择源提供商(AWS、GCS、Azure)
- 输入凭证和存储桶名称
- 开始迁移
生命周期规则
自动删除或转换存储类别:
Wrangler:
wrangler r2 bucket lifecycle put my-bucket --rules '[
{
"action": {"type": "AbortIncompleteMultipartUpload"},
"filter": {},
"abortIncompleteMultipartUploadDays": 7
},
{
"action": {"type": "Transition", "storageClass": "InfrequentAccess"},
"filter": {"prefix": "archives/"},
"daysFromCreation": 90
}
]'
事件通知
在存储桶事件上触发Workers:
Wrangler:
wrangler r2 bucket notification create my-bucket \
--queue=my-queue \
--event-type=object-create
支持事件:
object-create- 新上传object-delete- 删除
消息格式:
{
"account": "account-id",
"bucket": "my-bucket",
"object": {"key": "file.txt", "size": 1024, "etag": "..."},
"action": "PutObject",
"eventTime": "2024-01-15T12:00:00Z"
}
最佳实践
性能
- 对频繁访问对象使用Cloudflare缓存与自定义域名
- 对文件 >100MB 使用多部分上传(更快、更可靠)
- 使用Rclone进行批量操作(并发传输)
- 位置提示匹配用户地理位置
安全
- 切勿将访问密钥提交到版本控制
- 使用环境变量存储凭证
- 存储桶范围令牌以最小权限
- 预签名URL用于临时访问
- 启用Cloudflare Access以额外保护
成本优化
- 对归档使用不频繁访问存储(30天以上保留)
- 生命周期规则自动转换或删除
- 更大得多部分块 = 更少的A类操作
- 通过仪表板分析监控使用
命名
- 存储桶名称:小写、连字符、3-63字符
- 避免顺序前缀以更好性能(例如,使用哈希前缀)
- 如果使用带TLS的自定义域名,存储桶名称中无点
限制
- 每个账户的存储桶数: 1,000
- 对象大小: 最大5TB
- 存储桶名称: 3-63字符
- 生命周期规则: 每个存储桶1,000
- 事件通知规则: 每个存储桶100
- r2.dev速率限制: 1,000 请求/分钟(生产使用自定义域名)
故障排除
401 未授权:
- 验证访问密钥正确
- 检查端点URL包含账户ID
- 确保区域为“auto”用于大多数操作
403 禁止访问:
- 检查存储桶权限和令牌范围
- 验证CORS配置以用于浏览器请求
- 确认存储桶存在且名称正确
404 未找到:
- 对象键大小写敏感
- 检查存储桶名称拼写
- 验证对象上传成功
预签名URL不工作:
- 验证CORS配置
- 检查URL过期时间
- 确保源精确匹配CORS规则
多部分上传失败:
- 部分大小必须为5MiB - 5GiB
- 每次上传最多10,000部分
- 在7天内完成上传(或配置生命周期)
参考文件
详细文档见:
references/api-reference.md- 完整API端点文档references/sdk-examples.md- 所有语言的SDK示例references/workers-patterns.md- 高级Workers集成模式references/pricing-guide.md- 详细定价和成本优化