CloudflareR2对象存储实施技能Skill cloudflare-r2

本技能提供Cloudflare R2的完整指南,帮助用户实现S3兼容的对象存储服务,具有零出口带宽费用,适用于文件存储、数据迁移、与Cloudflare Workers无服务器集成等场景。关键词包括:Cloudflare R2, 对象存储, 云存储, 零费用, S3兼容, 文件上传下载, 数据迁移, Workers集成, 无服务器架构。

Serverless 0 次安装 0 次浏览 更新于 3/18/2026

名称: 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天最低保留期

访问方法

  1. R2 Workers绑定 - 无服务器集成(推荐新应用)
  2. S3 API - 与现有工具兼容
  3. 公共存储桶 - 通过自定义域名或r2.dev直接HTTP访问
  4. 预签名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

自定义域名(推荐生产):

  1. 仪表板 → R2 → 存储桶 → 设置 → 公共访问
  2. 添加自定义域名
  3. 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(批量、一次性)

最佳用于:完整迁移,已知对象列表

  1. 仪表板 → R2 → 数据迁移 → Super Slurper
  2. 选择源提供商(AWS、GCS、Azure)
  3. 输入凭证和存储桶名称
  4. 开始迁移

生命周期规则

自动删除或转换存储类别:

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 - 详细定价和成本优化

额外资源