与 Workers 绑定集成
S3 API 和预签名 URL 都走公网,要带 Access Key 鉴权。如果业务跑在 Cloudflare Workers 上,有一种更直接的方式,用 R2 绑定,Worker 代码里直接操作对象,不经过 S3 协议,不需要密钥。
Workers 是什么
Cloudflare Workers(Cloudflare 的边缘计算函数服务)是一种 Serverless 平台,代码部署后跑在全球 300 多个边缘节点上,离用户最近的节点响应请求。它适合做 API 网关、边缘渲染、CDN 回源逻辑、文件处理这类短任务。
Workers 用 JavaScript 写,运行时基于 V8 引擎,启动几乎零延迟。和传统服务器不同,它按请求计费,不跑的时候不计费。
R2 和 Workers 同属 Cloudflare 平台,两者之间有专门的内部通道,比走公网 S3 API 更快也更省事。
R2 绑定概念
绑定(Binding)是 Workers 的一种资源挂载机制。把一个 R2 Bucket 绑定到 Worker,Worker 代码里就能通过一个变量直接读写对象,不用配置 Access Key,不用拼端点地址。
| 对比项 | S3 API 接入 | R2 绑定 |
|---|---|---|
| 鉴权 | Access Key 凭证 | 无需凭证,平台内部信任 |
| 网络路径 | 走公网 | 走内部通道 |
| 延迟 | 受公网影响 | 极低 |
| 配置 | 代码里写端点和密钥 | wrangler.toml 声明 |
| 可用方法 | 完整 S3 API | get put delete head list |
绑定的代价是只能用在 Workers 里,不能拿到外部程序用。所以应用后端走 S3 API,边缘函数走绑定,两者各司其职。
配置绑定
在 Worker 项目的 wrangler.toml 里声明绑定
name = "image-worker"
main = "src/index.js"
compatibility_date = "2024-09-01"
[[r2_buckets]]
binding = "IMAGES"
bucket_name = "my-assets"
字段说明
| 字段 | 作用 |
|---|---|
| binding | 代码里访问用的变量名,例如 IMAGES |
| bucket_name | R2 里 Bucket 的实际名称 |
配置好后部署 Worker,绑定就生效了。
绑定对象方法
Worker 代码里通过 env.IMAGES 访问绑定对象,它有几个核心方法
| 方法 | 作用 | 返回值 |
|---|---|---|
| get | 读取对象 | R2ObjectBody 或 null |
| put | 写入对象 | R2Object |
| delete | 删除对象 | void |
| head | 只取元数据不取内容 | R2Object 或 null |
| list | 列出对象 | R2Objects |
这些方法都是异步的,要 await。和 S3 SDK 比起来,API 更简洁,参数也更少。
文件读写示例
下面是一个完整的 Worker,处理文件的上传、下载、删除
export default {
async fetch(request, env) {
const url = new URL(request.url);
const key = url.pathname.slice(1); // 去掉开头的斜杠
// 下载
if (request.method === "GET") {
const object = await env.IMAGES.get(key);
if (object === null) {
return new Response("Not Found", { status: 404 });
}
const headers = new Headers();
// writeHttpMetadata 会把对象的 ContentType 等写入响应头
object.writeHttpMetadata(headers);
headers.set("Cache-Control", "public, max-age=31536000");
return new Response(object.body, { headers });
}
// 上传
if (request.method === "PUT") {
await env.IMAGES.put(key, request.body, {
httpMetadata: {
contentType: request.headers.get("Content-Type"),
},
});
return new Response("Uploaded", { status: 201 });
}
// 删除
if (request.method === "DELETE") {
await env.IMAGES.delete(key);
return new Response("Deleted");
}
return new Response("Method Not Allowed", { status: 405 });
},
};
几个关键点。
object.body 是一个 ReadableStream,直接传给 Response 就能流式返回大文件,不用全读进内存。
writeHttpMetadata 把上传时存的 ContentType、CacheLanguage 等元数据写进响应头,省得手动设。
put 的第二个参数接受 ReadableStream、ArrayBuffer、字符串,所以 request.body 可以直接塞进去。
列出对象
list 方法用来查看 Bucket 里有哪些对象
const listed = await env.IMAGES.list({
prefix: "uploads/",
limit: 100,
});
for (const obj of listed.objects) {
console.log(obj.key, obj.size, obj.uploaded);
}
// 如果结果被截断,用 cursor 取下一页
if (listed.truncated) {
const next = await env.IMAGES.list({
prefix: "uploads/",
limit: 100,
cursor: listed.cursor,
});
}
参数说明
| 参数 | 作用 |
|---|---|
| prefix | 只列出指定前缀的对象 |
| limit | 单次返回数量上限,最大 1000 |
| cursor | 分页游标,从上一次结果取 |
图片处理实战
下面是一个实际场景。前端上传图片到 Worker,Worker 把原图存进 R2,再返回一个可访问的下载地址。配合自定义域名,整个图片站就搭起来了。
export default {
async fetch(request, env) {
const url = new URL(request.url);
// 上传图片 POST /upload
if (url.pathname === "/upload" && request.method === "POST") {
const formData = await request.formData();
const file = formData.get("file");
// 用时间戳和原扩展名拼 Key,避免覆盖
const ext = file.name.split(".").pop();
const key = `images/${Date.now()}.${ext}`;
await env.IMAGES.put(key, file, {
httpMetadata: {
contentType: file.type,
cacheControl: "public, max-age=31536000",
},
});
return Response.json({ url: `/${key}` });
}
// 访问图片 GET /images/xxx.png
const key = url.pathname.slice(1);
const object = await env.IMAGES.get(key);
if (object === null) {
return new Response("Not Found", { status: 404 });
}
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set("Cache-Control", "public, max-age=31536000");
return new Response(object.body, { headers });
},
};
部署后绑定自定义域名(例如 img.example.com),上传图片返回 /images/1731234567.png,前端拼成 https://img.example.com/images/1731234567.png 就能直接显示。
这个例子里 Worker 同时承担了上传入口和图片分发两个角色,R2 负责持久化存储,职责分明,结构简单。
小结
R2 绑定让 Workers 不带密钥就能直接读写对象,延迟低、配置简单。文件读写走 env.BINDING 的几个方法,图片站这类场景用一个 Worker 就能把上传、存储、分发全串起来。R2 教程到这里就结束了,从基础概念到 S3 API、公开访问、预签名 URL、Workers 集成,覆盖了日常开发的主要用法。
上一篇 预签名 URL