Cloudflare R2 存储详细使用指南
Cloudflare R2 存储的详细使用指南。
Cloudflare R2 是一种与 Amazon S3 API 高度兼容的对象存储服务。它的主要卖点之一是**零出口(egress)费用**,这意味着您从 R2 下载数据到互联网时,Cloudflare 不会收取带宽费用(在合理使用范围内)。这使其成为存储大量数据、静态资源(如图片、视频、前端构建文件)以及与 Cloudflare Workers 集成的理想选择。
**核心概念:**
* **存储桶 (Buckets):** R2 中的基本存储容器。每个存储桶都有一个全局唯一的名称。您的对象(文件)存储在存储桶中。
* **对象 (Objects):** 您存储在 R2 中的文件(例如,图片、视频、文档、备份文件等)。每个对象都有一个唯一的键(key,即文件名或路径)和一个值(文件内容)。
* **键 (Key):** 对象在存储桶中的唯一标识符,类似于文件名或文件路径 (e.g., `images/profile.jpg`, `backup/2023-10-26.zip`)。
* **S3 兼容 API:** R2 实现了 Amazon S3 API 的一个子集,这意味着您可以使用许多现有的 S3 工具和 SDK 与 R2 交互。
* **零出口费用:** 从 R2 下载数据到公共互联网通常不收取带宽费用,这是相对于 AWS S3 等服务的一个显著优势。
* **与 Workers 集成:** R2 可以轻松地与 Cloudflare Workers 绑定,允许您通过边缘计算动态处理和提供存储在 R2 中的对象。
**详细使用指南:**
### 1. 准备工作
* **Cloudflare 账户:** 您需要一个 Cloudflare 账户。
* **Wrangler CLI (推荐):** Cloudflare 的命令行工具,用于管理 Workers 和 R2 存储桶(特别是与 Workers 集成时)。
* 安装: `npm install -g wrangler`
* 登录: `wrangler login`
* **S3 兼容工具 (可选):** 如 AWS CLI, rclone, Cyberduck, S3 Browser, 或各种编程语言的 S3 SDK。
### 2. 创建 R2 存储桶
您可以通过 Cloudflare 仪表板或 Wrangler CLI 创建 R2 存储桶。
* **通过 Cloudflare 仪表板:**
1. 登录到您的 Cloudflare 账户。
2. 在左侧导航栏中,找到并点击 "R2"。
3. 点击 "创建存储桶 (Create bucket)"。
4. 输入一个**全局唯一**的存储桶名称(例如 `my-unique-app-assets`)。
5. 选择存储桶的**存储位置提示 (Location Hint)**。这有助于 Cloudflare 将数据存储在靠近您预期访问模式的区域,但 R2 旨在提供全球访问。选择一个离您的主要用户或数据源近的位置。
6. 点击 "创建存储桶 (Create bucket)"。
* **通过 Wrangler CLI:**
```bash
wrangler r2 bucket create <YOUR_BUCKET_NAME> [--location-hint <HINT>]
```
例如:
```bash
wrangler r2 bucket create my-unique-app-assets --location-hint APAC
```
可用的位置提示 (Location Hints) 包括:`APAC` (亚太), `EEUR` (东欧), `ENAM` (北美东部), `WEUR` (西欧), `WNAM` (北美西部),或者留空让 Cloudflare 自动选择。
### 3. 配置 S3 API 访问 (用于第三方工具)
如果您想使用 S3 兼容的工具(如 AWS CLI, rclone)或 SDK 访问 R2,您需要生成 API 令牌。
1. 在 Cloudflare 仪表板的 R2 概览页面,点击 "管理 R2 API 令牌 (Manage R2 API Tokens)"。
2. 点击 "创建 API 令牌 (Create API token)"。
3. 给令牌一个描述性的名称。
4. 选择权限:
* **仅对象读取 (Object Read Only):** 只允许下载和列出对象。
* **对象读写 (Object Read & Write):** 允许上传、下载、列出、删除对象。
5. 选择要应用此令牌的存储桶(可以是特定存储桶或所有存储桶)。
6. (可选)设置 TTL(令牌有效期)。
7. 点击 "创建 API 令牌 (Create API token)"。
8. **立即复制并安全地保存 `访问密钥 ID (Access Key ID)` 和 `机密访问密钥 (Secret Access Key)`。机密访问密钥只显示一次。**
配置 S3 兼容工具时,您还需要您的**账户 ID (Account ID)**。您可以在 Cloudflare 仪表板的任何域的 "概述 (Overview)" 页面的右下角找到它,或者在 R2 页面的 "R2 API 令牌" 部分的顶部找到。
**S3 端点 (Endpoint):**
当配置 S3 工具时,您需要指定 R2 的 S3 API 端点。格式通常是:
`https://<ACCOUNT_ID>.r2.cloudflarestorage.com`
**示例:配置 AWS CLI**
```bash
aws configure --profile r2-profile
AWS Access Key ID [None]: YOUR_R2_ACCESS_KEY_ID
AWS Secret Access Key [None]: YOUR_R2_SECRET_ACCESS_KEY
Default region name [None]: auto # 或任何有效区域,例如 us-east-1,R2 会忽略它但 CLI 可能需要
Default output format [None]: json # 或 text, table
# 然后使用 --endpoint-url 和 --profile
aws s3 ls --endpoint-url https://<ACCOUNT_ID>.r2.cloudflarestorage.com --profile r2-profile
aws s3 ls s3://my-unique-app-assets --endpoint-url https://<ACCOUNT_ID>.r2.cloudflarestorage.com --profile r2-profile
aws s3 cp my-local-file.txt s3://my-unique-app-assets/my-remote-file.txt --endpoint-url https://<ACCOUNT_ID>.r2.cloudflarestorage.com --profile r2-profile
```
### 4. 使用 Wrangler CLI 与 R2 交互
Wrangler CLI 提供了直接与 R2 存储桶交互的命令,无需配置 S3 凭证(因为它使用您的 Cloudflare 登录会话)。
* **列出存储桶:**
```bash
wrangler r2 bucket list
```
* **上传对象:**
```bash
wrangler r2 object put <BUCKET_NAME>/<OBJECT_KEY> --file <PATH_TO_LOCAL_FILE> [--content-type <MIME_TYPE>]
```
例如:
```bash
wrangler r2 object put my-unique-app-assets/images/logo.png --file ./local/images/logo.png --content-type image/png
```
* **下载对象:**
```bash
wrangler r2 object get <BUCKET_NAME>/<OBJECT_KEY> --file <PATH_TO_SAVE_LOCALLY>
```
例如:
```bash
wrangler r2 object get my-unique-app-assets/images/logo.png --file ./downloaded-logo.png
```
* **列出对象:**
```bash
wrangler r2 object list <BUCKET_NAME> [--prefix <PREFIX>]
```
例如,列出 `images/` 目录下的所有对象:
```bash
wrangler r2 object list my-unique-app-assets --prefix images/
```
* **删除对象:**
```bash
wrangler r2 object delete <BUCKET_NAME>/<OBJECT_KEY>
# 或删除多个对象
wrangler r2 object delete <BUCKET_NAME>/<OBJECT_KEY_1> <BUCKET_NAME>/<OBJECT_KEY_2>
```
例如:
```bash
wrangler r2 object delete my-unique-app-assets/images/old-logo.png
```
* **删除存储桶 (存储桶必须为空):**
```bash
wrangler r2 bucket delete <BUCKET_NAME>
```
### 5. 将 R2 与 Cloudflare Workers 集成
这是 R2 最强大的用途之一。您可以将 R2 存储桶绑定到 Worker,然后在 Worker 代码中直接访问它。
1. **在 `wrangler.toml` 中配置绑定:**
在您的 Worker 项目的 `wrangler.toml` 文件中,添加一个 R2 存储桶绑定:
```toml
name = "my-worker-project"
main = "src/index.ts" # 或 .js
compatibility_date = "2023-10-30" # 使用较新的日期
# ... 其他配置 ...
[[r2_buckets]]
binding = "MY_BUCKET" # Worker 代码中将使用此名称访问 R2 存储桶
bucket_name = "my-unique-app-assets" # 您在 Cloudflare 上创建的 R2 存储桶的名称
# preview_bucket_name = "my-preview-bucket" # (可选) 用于 wrangler dev --remote 的预览存储桶
```
`binding`: 这是您在 Worker 代码中引用 R2 存储桶时将使用的变量名。
2. **在 Worker 代码中访问 R2:**
您的 Worker 脚本 (e.g., `src/index.ts`) 将通过 `env` 对象上的绑定名称访问 R2。
```typescript
// src/index.ts
export interface Env {
MY_BUCKET: R2Bucket; // 'MY_BUCKET' 必须与 wrangler.toml 中的 binding 名称匹配
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
const objectKey = url.pathname.slice(1); // 从路径获取对象键,例如 /my-image.png -> my-image.png
switch (request.method) {
case "PUT":
// 上传对象
if (!objectKey) {
return new Response("Object key is required in path", { status: 400 });
}
if (!request.body) {
return new Response("Request body is required for PUT", { status: 400 });
}
try {
const object = await env.MY_BUCKET.put(objectKey, request.body, {
httpMetadata: request.headers, // 复制客户端的 Content-Type, Content-Encoding 等
// customMetadata: { uploadedBy: "worker" }, // 可选的自定义元数据
});
return new Response(`Object ${object.key} uploaded successfully! Version: ${object.version}`, { status: 201 });
} catch (e: any) {
return new Response(`Error uploading object: ${e.message}`, { status: 500 });
}
case "GET":
// 获取对象
if (!objectKey) {
// 如果没有指定键,可以列出对象或返回错误
const options: R2ListOptions = {
prefix: url.searchParams.get('prefix') ?? undefined,
delimiter: url.searchParams.get('delimiter') ?? undefined,
cursor: url.searchParams.get('cursor') ?? undefined,
include: ['httpMetadata', 'customMetadata'], // 可选
};
const listing = await env.MY_BUCKET.list(options);
return new Response(JSON.stringify(listing), { headers: { 'content-type': 'application/json;charset=UTF-8' } });
}
const object = await env.MY_BUCKET.get(objectKey);
if (object === null) {
return new Response("Object Not Found", { status: 404 });
}
const headers = new Headers();
object.writeHttpMetadata(headers); // 设置 Content-Type, ETag 等
headers.set("etag", object.httpEtag);
// headers.set("Cache-Control", "public, max-age=3600"); // 可选:添加缓存头
return new Response(object.body, {
headers,
});
case "DELETE":
// 删除对象
if (!objectKey) {
return new Response("Object key is required for DELETE", { status: 400 });
}
try {
await env.MY_BUCKET.delete(objectKey);
return new Response(`Object ${objectKey} deleted successfully`);
} catch (e: any) {
return new Response(`Error deleting object: ${e.message}`, { status: 500 });
}
default:
return new Response("Method Not Allowed", { status: 405 });
}
},
};
```
**R2Bucket API 方法 (在 Worker 中):**
* `get(key: string, options?: R2GetOptions): Promise<R2Object | R2ObjectBody | null>`: 获取对象。
* `put(key: string, value: ReadableStream | ArrayBuffer | string | Blob, options?: R2PutOptions): Promise<R2Object>`: 上传对象。
* `httpMetadata`: 可以设置 Content-Type, Cache-Control 等 HTTP 头。
* `customMetadata`: 存储自定义键值对元数据。
* `delete(keys: string | string[]): Promise<void>`: 删除一个或多个对象。
* `list(options?: R2ListOptions): Promise<R2ListObjectsV2Output>`: 列出存储桶中的对象。
* `prefix`: 过滤具有特定前缀的对象。
* `delimiter`: 用于模拟目录结构。
* `cursor`: 用于分页。
* `limit`: 每页返回的对象数量。
* `head(key: string): Promise<R2Object | null>`: 获取对象的元数据而不下载其内容。
* `createMultipartUpload(key: string, options?: R2MultipartOptions): Promise<R2MultipartUpload>`: 开始分段上传(用于大文件)。
* `resumeMultipartUpload(key: string, uploadId: string): R2MultipartUpload`: 恢复分段上传。
### 6. 公开访问 R2 存储桶中的对象
默认情况下,R2 存储桶是私有的。您可以通过以下方式公开它们:
* **1. 通过自定义域名 (推荐):**
这是最灵活和推荐的方式,允许您使用自己的域名 (例如 `assets.yourdomain.com`) 来提供 R2 中的对象,并可以利用 Cloudflare 的缓存和其他功能。
1. 在 Cloudflare 仪表板中,转到您的域。
2. 在左侧导航栏中,找到 "R2"。
3. 在 R2 页面,找到您要公开的存储桶,点击其名称。
4. 转到 "设置 (Settings)" 标签页。
5. 在 "公共访问 (Public Access)" -> "通过 R2.dev 公开 (Public access via R2.dev)" 部分,点击 "允许访问 (Allow Access)"。**这会启用一个 `*.r2.dev` 的公共 URL,但通常您会想使用自己的域名。**
6. 在 "公共访问 (Public Access)" -> "连接的域 (Connected Domains)" 或 "自定义域 (Custom Domains)" 部分,点击 "连接域 (Connect Domain)" 或 "添加自定义域 (Add Custom Domain)"。
7. 输入您想用于访问此存储桶的子域名(例如 `r2-assets.yourdomain.com`)。Cloudflare 会自动为您处理 DNS 记录。
8. 一旦连接,`
https://r2-assets.yourdomain.com/object-key` 将公开提供对象。
9. 您可以配置缓存规则、转换规则等作用于此子域名。
* **2. 通过 `*.r2.dev` 公共 URL (谨慎使用):**
当您在存储桶设置中启用 "通过 R2.dev 公开 (Public access via R2.dev)" 时,您的存储桶会获得一个公共 URL,格式为 `https://pub-<RANDOM_ID>.r2.dev`。
* 您可以在存储桶的设置页面找到这个 URL。
* 对象可以通过 `https://pub-<RANDOM_ID>.r2.dev/object-key` 访问。
* **注意:** 此 URL 是公开的,任何知道它的人都可以访问您的对象。它不直接与您的 Cloudflare 区域 (zone) 绑定,因此缓存等功能可能不像自定义域那样直接可配置。
* **3. 通过 Worker (完全控制):**
如上面的 Worker 示例所示,您可以编写一个 Worker 来从 R2 获取对象并将其作为 HTTP 响应返回。这使您可以实现自定义授权、内容转换等。
### 7. 预签名 URL (Presigned URLs)
预签名 URL 允许您为私有对象生成一个临时的、有时间限制的公共访问链接。这对于允许用户临时下载或上传特定对象非常有用,而无需公开整个存储桶或管理复杂的用户权限。
* **在 Worker 中生成预签名 URL:**
```typescript
// src/index.ts (部分示例)
export interface Env {
MY_BUCKET: R2Bucket;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
const objectKey = "private/document.pdf"; // 假设这是您要为其生成链接的对象
if (url.pathname === "/generate-download-link") {
try {
const signedUrl = await env.MY_BUCKET.createSignedUrl('getObject', objectKey, {
// S3 兼容的 API 操作: 'getObject', 'putObject'
// expires: 3600, // URL 有效期(秒),默认 3600 (1小时)
// Можно также указать другие параметры, такие как `range`, `contentDisposition` и т.д.
});
return new Response(JSON.stringify({ downloadUrl: signedUrl }), {
headers: { "Content-Type": "application/json" },
});
} catch (e: any) {
return new Response(`Error generating signed URL: ${e.message}`, { status: 500 });
}
}
// ... 其他路由 ...
return new Response("Not Found", { status: 404 });
},
};
```
然后,客户端可以使用此 `signedUrl` 在有效期内下载对象。
### 8. 定价
R2 的定价通常基于:
* **存储量:** 每月存储的数据总量 (GB)。
* **A 类操作 (Class A Operations):** 主要是写入和列出操作 (如 PUT, LIST, COPY)。
* **B 类操作 (Class B Operations):** 主要是读取操作 (如 GET, HEAD)。
**关键优势:零出口费用。**
Cloudflare 通常会提供一个免费套餐,包含一定量的存储和操作数。超出部分按使用量计费。请务必查阅 [Cloudflare R2 官方定价页面] 以获取最新和最准确的信息。
### 9. 最佳实践和注意事项
* **存储桶命名:** 使用有意义且符合 DNS 命名约定的存储桶名称。
* **对象键命名:** 使用 `/` 来模拟目录结构,方便组织和列出对象 (例如 `
images/avatars/user123.jpg`)。
* **安全性:**
* 默认情况下存储桶是私有的。仅在必要时公开访问。
* 优先使用自定义域进行公共访问,以便更好地控制和集成 Cloudflare 功能。
* 安全地管理您的 R2 API 令牌。为不同的应用程序或用途使用具有最小必要权限的不同令牌。
* 对于临时访问,请使用预签名 URL。
* **内容类型 (Content-Type):** 上传对象时,设置正确的 `Content-Type` HTTP 头 (MIME 类型),以便浏览器能正确处理文件。Wrangler 和 Worker 的 `put` 方法都允许您设置它。
* **缓存:** 如果通过自定义域公开 R2 对象,请配置 Cloudflare 缓存规则以提高性能并减少对 R2 的直接请求。
* **错误处理:** 在 Worker 代码中妥善处理 R2 API 调用可能发生的错误。
* **大文件上传:** 对于非常大的文件,考虑使用分段上传 (Multipart Uploads)。Worker 中的 `createMultipartUpload` 方法支持此功能,许多 S3 SDK 和工具也支持。
* **数据一致性:** R2 提供强最终一致性。在写入新对象或覆盖/删除现有对象后,后续的读取操作(包括GET、HEAD和LIST)将立即反映这些更改。
* **版本控制 (Versioning):** R2 支持对象版本控制。您可以在存储桶设置中启用它。启用后,对对象的每次修改(上传新版本或删除)都会创建一个新版本,而不是直接覆盖或永久删除。这有助于防止意外删除或覆盖,并允许您恢复到对象的先前版本。
* **对象生命周期管理 (Object Lifecycle Management):** R2 支持生命周期规则,允许您自动管理对象。例如,您可以设置规则在对象达到一定年龄后将其删除,或者(如果未来支持)将其转移到不同的存储类别。
### 10. 总结
Cloudflare R2 是一个强大且经济高效的对象存储解决方案,特别是当您需要处理大量数据出口或希望与 Cloudflare 生态系统(尤其是 Workers)紧密集成时。其 S3 兼容性使其易于采用,而零出口费用是一个巨大的吸引力。
请始终参考 [Cloudflare R2 官方文档] 以获取最新的功能、限制和最佳实践信息。