CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Cloudflare 实战入门/课时/R2 对象存储
RawBlame
课时目录12 节

Cloudflare 实战入门

  1. 01边缘计算概览
  2. 02Wrangler 与本地开发
  3. 03Workers 核心
  4. 04Workers KV
  5. 05R2 对象存储
  6. 06D1 数据库
  7. 07Queues 消息队列
  8. 08Durable Objects
  9. 09Workers AI
  10. 10Stream 与媒体
  11. 11安全与防护
  12. 12运维与集成

R2 对象存储

S3 兼容存储与生命周期。

30 分钟

R2 对象存储

这一课你将能做到

  1. 创建 R2 Bucket 并在 Worker 中完成对象上传、下载、删除、列表操作
  2. 生成预签名 URL(Presigned URL)实现前端直传,绕过 Worker 流量
  3. 配置 R2 生命周期规则(自动删除过期文件)和自定义域名公开访问

为什么要学

用户要上传头像、视频、备份文件。如果全走你的服务器,流量费爆炸,存储费也爆炸。如果直接传 S3,跨区域延迟和出口流量费一样吓人。R2 是 Cloudflare 推出的对象存储,兼容 S3 API,但零出口流量费——无论你从哪里下载,都不收流量钱。

人话:R2 就是一个「不限量 CDN 加速的 S3」。你可以把它当成图片、视频、备份文件的廉价仓库,配合 Worker 做签名、权限控制、自动处理(如图片压缩)。

先建立图像

用户浏览器
    │
    ├── 直接上传 ──> R2 Bucket (通过 Presigned URL)
    │
    └── 请求 Worker ──> 生成 Presigned URL ──> 返回给用户
              │
              └── 也可以直接读写 R2 (S3 API)
              
R2 Bucket
    ├── 自动同步到边缘节点 (读加速)
    ├── 生命周期规则 (30天后自动删除)
    └── 自定义域名 (cdn.example.com)

跟着做

步骤 1:创建 Bucket

npx wrangler r2 bucket create my-uploads

绑定到 Worker(wrangler.toml):

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

步骤 2:Worker 中上传/下载

export interface Env {
  MY_BUCKET: R2Bucket;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const key = url.searchParams.get('key');
    
    if (request.method === 'PUT' && key) {
      // 上传
      const body = await request.arrayBuffer();
      await env.MY_BUCKET.put(key, body, {
        httpMetadata: { contentType: request.headers.get('content-type') ?? 'application/octet-stream' },
        customMetadata: { uploadedBy: 'user123' },
      });
      return new Response('Uploaded', { status: 201 });
    }
    
    if (request.method === 'GET' && key) {
      // 下载
      const object = await env.MY_BUCKET.get(key);
      if (!object) return new Response('Not Found', { status: 404 });
      
      const headers = new Headers();
      object.writeHttpMetadata(headers);
      headers.set('etag', object.httpEtag);
      headers.set('Cache-Control', 'public, max-age=31536000');
      
      return new Response(object.body, { headers });
    }
    
    if (request.method === 'DELETE' && key) {
      await env.MY_BUCKET.delete(key);
      return new Response('Deleted');
    }
    
    return new Response('Bad Request', { status: 400 });
  },
} satisfies ExportedHandler<Env>;

步骤 3:生成预签名 URL(前端直传)

前端直传可以避免 Worker 成为流量瓶颈。用 @aws-sdk/s3-request-presigner:

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

export interface Env {
  R2_ACCOUNT_ID: string;
  R2_ACCESS_KEY_ID: string;
  R2_SECRET_ACCESS_KEY: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const key = url.searchParams.get('key');
    if (!key) return new Response('Missing key', { status: 400 });
    
    const s3 = new S3Client({
      region: 'auto',
      endpoint: `https://${env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
      credentials: {
        accessKeyId: env.R2_ACCESS_KEY_ID,
        secretAccessKey: env.R2_SECRET_ACCESS_KEY,
      },
    });
    
    const presignedUrl = await getSignedUrl(
      s3,
      new PutObjectCommand({ Bucket: 'my-uploads', Key: key, ContentType: 'image/jpeg' }),
      { expiresIn: 3600 }
    );
    
    return Response.json({ url: presignedUrl });
  },
} satisfies ExportedHandler<Env>;

前端拿到 url 后直接 PUT 上传,不经过 Worker。

步骤 4:生命周期规则(自动清理)

npx wrangler r2 bucket lifecycle add my-uploads --expire-days 30 --prefix "tmp/"

这条命令给 tmp/ 前缀的对象设置 30 天自动过期。

步骤 5:自定义域名公开访问

在 Dashboard → R2 → 对应 Bucket → Settings → Public Access 中绑定自定义域名(如 cdn.example.com)。之后 https://cdn.example.com/image.jpg 就能直接访问,走 Cloudflare CDN 边缘缓存。

讲清楚要点

  1. R2 兼容 S3 API:你可以用 AWS SDK 操作 R2,只需把 endpoint 改成 https://<account_id>.r2.cloudflarestorage.com。现有 S3 迁移成本极低。
  2. 零出口流量费:这是 R2 最大的卖点。S3 下载 1TB 要收几十美元,R2 不收。但注意:R2 的存储费比 S3 略高,适合「读多写少、全球分发」的场景。
  3. Worker 直传 vs 预签名直传:小文件(< 5MB)可以走 Worker 中转,方便做鉴权和处理;大文件或高并发上传必须用预签名 URL,否则 Worker 的 CPU 时间和内存会成为瓶颈。
  4. R2 不是实时同步:写入后对象立即可读,但全球边缘缓存的更新有延迟(类似 CDN)。如果用户刚上传就要立刻下载,建议强制刷新 CDN 或直接用 r2.dev 域名(不缓存)。
  5. 生命周期规则按前缀匹配:可以给不同前缀设置不同过期策略。tmp/ 30 天,avatars/ 永不过期。

你可能会懵的地方

  • R2 和 KV 怎么选? 小数据(< 25MB)、高频读、需要 key-value 语义用 KV;大文件、图片、视频、备份用 R2。KV 的 value 有 25MB 上限,R2 单对象可达 5TB。

  • 预签名 URL 安全吗? 安全,但 URL 本身包含签名和过期时间。不要泄露给未授权用户;敏感操作(如删除)不要生成预签名 URL,让 Worker 做鉴权后再操作。

  • R2 有文件夹概念吗? 没有。对象存储是扁平的,avatars/user1.jpg 中的 avatars/ 只是 key 的一部分,不是真实目录。但你可以用 / 做前缀来模拟目录结构。

  • 怎么限制用户只能上传到自己的目录? 生成预签名 URL 时,把 Key 设为 uploads/${userId}/${filename},用户只能操作这个前缀下的对象。

  • R2 支持图片处理吗? 本身不支持。但你可以用 Cloudflare Images(付费)或 Worker + @cf-wasm/photon 等 WASM 库在边缘做缩放、格式转换。

错法对照

错法问题正解
前端直接把文件 POST 给 Worker 再转 R2大文件会撑爆 Worker 内存和 CPU用预签名 URL 让前端直传 R2
把 R2 当数据库用,存大量小 JSON成本高、延迟高小数据用 KV 或 D1
不设置 Cache-Control每次下载都回源 R2,浪费带宽下载时设置 public, max-age=31536000
用 r2.dev 域名做生产 CDNr2.dev 有速率限制,不适合生产绑定自定义域名,走 Cloudflare CDN
删除对象后立刻检查列表列表可能有延迟删除后直接返回成功,或加延迟重试

卡点急救

  1. 预签名 URL 上传 403:检查 ContentType 是否和实际上传的一致;检查 URL 是否过期;检查 Access Key 是否有 PutObject 权限。
  2. Worker 里 env.MY_BUCKET is undefined:检查 wrangler.toml 的 binding 名;重启 wrangler dev。
  3. 下载时中文文件名乱码:设置 content-disposition: attachment; filename*=UTF-8''${encodeURIComponent(filename)}。

面试常问

  • R2 和 S3 的核心区别是什么? R2 零出口流量费,S3 按下载流量收费;R2 深度集成 Cloudflare CDN,S3 需要额外配 CloudFront;R2 的 API 兼容 S3,但部分高级功能(如 S3 Object Lock)尚未支持。

  • 预签名 URL 的原理? 用 Access Key 对请求参数(Bucket、Key、过期时间、Content-Type)做 HMAC 签名,生成一个临时 URL。持有 URL 的人可以在过期前执行指定操作,无需知道真实密钥。

  • R2 怎么实现图片 CDN? 绑定自定义域名到 R2 Bucket,上传图片后通过 https://cdn.example.com/image.jpg 访问。Cloudflare 会自动做边缘缓存,配合 Worker 可以做动态缩放、格式转换(WebP/AVIF)。

自测三问

  1. 为什么大文件上传要用预签名 URL 而不是走 Worker 中转?
  2. R2 的「零出口流量费」是什么意思?什么场景下这个优势最明显?
  3. 如果用户上传了一个文件但立刻访问 404,可能是什么原因?

小结一句话

R2 是 S3 兼容的边缘对象存储,零出口流量费 + 全球 CDN 加速,配合预签名 URL 和 Worker 可以实现安全、低成本的大文件存储与分发。

延伸

  • R2 官方文档
  • R2 Pricing
  • 下一课:D1 数据库,学习边缘 SQLite 的 schema 设计与迁移。
加载本节练习…
← 上一课Workers KV下一课 →D1 数据库