跳到主要内容

R2 对象存储

Cloudflare R2 是一个对象存储服务,兼容 AWS S3 API,最大的特点是零出口流量费用。它非常适合存储图片、视频、备份文件等大量数据。

R2 简介

什么是 R2?

R2 是 Cloudflare 提供的对象存储服务,让你能够存储大量非结构化数据(图片、视频、文档等)。与传统对象存储不同,R2 不收取出口流量费用。

R2 与 S3 对比

特性Cloudflare R2AWS S3
存储费用$0.015/GB/月$0.023/GB/月
出口流量免费$0.09/GB
APIS3 兼容原生 S3
CDN 集成内置需额外配置 CloudFront

免费额度

资源免费额度
存储空间10GB
A 类操作(写入)100 万次/月
B 类操作(读取)1000 万次/月

R2 的优势

零出口费用:这是 R2 最大的优势。传统云存储按流量收费,R2 完全免费。

S3 兼容:使用现有的 S3 SDK 和工具,无需修改代码。

全球加速:通过 Cloudflare CDN 加速访问。

与 Workers 集成:可以在边缘直接操作 R2。

快速开始

创建存储桶

  1. 登录 Cloudflare 控制台
  2. 进入 "R2 Object Storage"
  3. 点击 "Create bucket"
  4. 输入存储桶名称
  5. 选择位置提示(可选)
  6. 点击 "Create bucket"

存储桶命名规则

  • 长度:3-63 个字符
  • 只能包含小写字母、数字和连字符
  • 必须以字母或数字开头和结尾
  • 不能使用 IP 地址格式

公开访问

默认情况下,存储桶是私有的。要公开访问:

  1. 进入存储桶设置
  2. 点击 "Settings" 标签
  3. 在 "Public access" 部分,点击 "Allow Access"
  4. 配置自定义域名或使用 R2 公开 URL

配置自定义域名

步骤一:添加域名

  1. 进入存储桶的 "Settings"
  2. 在 "Custom domains" 部分,点击 "Connect Domain"
  3. 输入域名(如 cdn.example.com
  4. 如果域名托管在 Cloudflare,会自动配置 DNS

步骤二:验证

配置完成后,可以通过自定义域名访问文件:

https://cdn.example.com/image.jpg

API 访问

创建 API 令牌

  1. 进入 "R2 Object Storage"
  2. 点击 "Manage R2 API Tokens"
  3. 点击 "Create API token"
  4. 设置权限和存储桶访问范围
  5. 保存 Access Key ID 和 Secret Access Key

使用 AWS SDK

R2 兼容 S3 API,可以使用 AWS SDK:

import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';

const client = new S3Client({
region: 'auto',
endpoint: 'https://<account-id>.r2.cloudflarestorage.com',
credentials: {
accessKeyId: 'your-access-key',
secretAccessKey: 'your-secret-key',
},
});

// 上传文件
await client.send(new PutObjectCommand({
Bucket: 'my-bucket',
Key: 'image.jpg',
Body: fileBuffer,
ContentType: 'image/jpeg',
}));

// 下载文件
const response = await client.send(new GetObjectCommand({
Bucket: 'my-bucket',
Key: 'image.jpg',
}));

使用 Python boto3

import boto3

s3 = boto3.client(
's3',
endpoint_url='https://<account-id>.r2.cloudflarestorage.com',
aws_access_key_id='your-access-key',
aws_secret_access_key='your-secret-key',
region_name='auto'
)

# 上传文件
s3.upload_file('local-file.jpg', 'my-bucket', 'remote-file.jpg')

# 下载文件
s3.download_file('my-bucket', 'remote-file.jpg', 'local-file.jpg')

在 Workers 中使用 R2

绑定 R2 存储桶

wrangler.toml 中配置:

[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"

上传文件

export default {
async fetch(request, env, ctx) {
const formData = await request.formData();
const file = formData.get('file');

await env.MY_BUCKET.put(file.name, file.stream(), {
httpMetadata: {
contentType: file.type,
},
});

return Response.json({ success: true, key: file.name });
},
};

下载文件

export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const key = url.pathname.slice(1);

const object = await env.MY_BUCKET.get(key);

if (!object) {
return new Response('Not Found', { status: 404 });
}

return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata.contentType,
'Cache-Control': 'public, max-age=31536000',
},
});
},
};

列出文件

export default {
async fetch(request, env, ctx) {
const listed = await env.MY_BUCKET.list();

return Response.json({
objects: listed.objects.map(obj => ({
key: obj.key,
size: obj.size,
uploaded: obj.uploaded,
})),
});
},
};

删除文件

await env.MY_BUCKET.delete('file.jpg');

图床实战

创建图床 Worker

export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);

if (request.method === 'POST' && url.pathname === '/upload') {
const formData = await request.formData();
const file = formData.get('file');

if (!file || !file.type.startsWith('image/')) {
return new Response('Invalid file', { status: 400 });
}

const key = `images/${Date.now()}-${file.name}`;
await env.MY_BUCKET.put(key, file.stream(), {
httpMetadata: { contentType: file.type },
});

return Response.json({
url: `https://cdn.example.com/${key}`,
key,
});
}

if (request.method === 'GET') {
const key = url.pathname.slice(1);
const object = await env.MY_BUCKET.get(key);

if (!object) {
return new Response('Not Found', { status: 404 });
}

return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata.contentType,
'Cache-Control': 'public, max-age=31536000',
},
});
}

return new Response('Method Not Allowed', { status: 405 });
},
};

CORS 配置

设置 CORS 规则

在存储桶设置中配置 CORS:

[
{
"AllowedOrigins": ["https://example.com"],
"AllowedMethods": ["GET", "PUT"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3600
}
]

生命周期规则

可以设置自动删除或转移旧文件的规则:

  1. 进入存储桶设置
  2. 点击 "Object lifecycle rules"
  3. 创建规则设置过期时间

常见问题

如何迁移现有数据?

使用 rclone 或 AWS CLI 同步数据:

# 配置 rclone
rclone config

# 同步数据
rclone sync s3:source-bucket r2:target-bucket

如何防止盗链?

配置 CORS 规则,只允许特定域名访问,或使用 Workers 检查 Referer。

文件大小限制?

单文件最大 5TB,但上传超过 5GB 需要使用分片上传。

参考链接

下一步

完成 R2 学习后,接下来学习 D1 数据库,了解如何使用边缘数据库。