Share

外观
风格

Cloudflare Worker + R2:让 AI 帮你部署后端

2026年2月8日 · 专栏

封面图

Cloudflare Worker + R2:让 AI 帮你部署后端

想搞个小工具——图床、短链、API 代理——但不想维护服务器。VPS 要续费、要装 Nginx、要配证书、要防 DDoS……太累了。

后来发现 Cloudflare Worker + R2 这套组合:代码扔上去就跑,存储按用量付费,免费额度够个人用,而且 CLI 操作简单到 AI 都能帮你部署。

现在我的图床、文章发布、API 网关全跑在上面,一年成本 $0。


这是什么

Worker:Cloudflare 的 Serverless 函数,类似 AWS Lambda,但:

  • 冷启动快(< 5ms)
  • 免费 10 万次/天
  • 全球边缘节点,延迟低
  • 支持自定义域名

R2:Cloudflare 的对象存储,兼容 S3 API,但:

  • 免出口流量费(S3 最贵的就是这个)
  • 免费 10 GB 存储 + 100 万次请求/月
  • Worker 里直接绑定,不用配 SDK

Wrangler:Cloudflare 的 CLI 工具,本地开发、部署、管理全靠它。


效果展示

部署一个图床:

# 创建 R2 存储桶
wrangler r2 bucket create images

# 部署 Worker
wrangler deploy

# 上传图片测试
curl -X POST https://img.example.com/upload \
  -H "Authorization: Bearer sk-xxx" \
  -F "file=@screenshot.png"
# 返回:{"url": "https://img.example.com/abc123.png"}

从写代码到上线,10 分钟。


快速开始

1. 安装 Wrangler

# 推荐用 pnpm(也可以用 npm/bun)
pnpm add -g wrangler

# 验证安装
wrangler --version

2. 登录 Cloudflare

wrangler login

会打开浏览器授权,授权后本地就能操作你的 Cloudflare 账户了。

3. 创建第一个 Worker

mkdir my-api && cd my-api
wrangler init

选择 TypeScript,生成的项目结构:

my-api/
├── src/
│   └── index.ts      # 入口文件
├── wrangler.toml     # 配置文件
└── package.json

4. 写个 Hello World

src/index.ts

export default {
  async fetch(request: Request): Promise<Response> {
    return new Response("Hello from Cloudflare Worker!");
  },
};

5. 本地调试

wrangler dev

浏览器打开 http://localhost:8787,看到 Hello World。

6. 部署上线

wrangler deploy

返回一个 *.workers.dev 的 URL,直接能访问。


绑定 R2 存储

1. 创建 Bucket

wrangler r2 bucket create my-bucket

2. 配置绑定

wrangler.toml

name = "my-api"
main = "src/index.ts"
compatibility_date = "2024-01-01"

<span class="private-link" title="未发布的笔记">r2_buckets</span>
binding = "BUCKET"      # 代码里用这个名字访问
bucket_name = "my-bucket"

3. 代码里使用

interface Env {
  BUCKET: R2Bucket;  // 类型定义
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (request.method === "PUT") {
      // 上传文件
      const body = await request.arrayBuffer();
      await env.BUCKET.put("hello.txt", body);
      return new Response("Uploaded!");
    }

    if (request.method === "GET") {
      // 下载文件
      const object = await env.BUCKET.get("hello.txt");
      if (!object) return new Response("Not found", { status: 404 });
      return new Response(object.body);
    }

    return new Response("Method not allowed", { status: 405 });
  },
};

R2 API 很简单:put()get()delete()list(),够用了。


设置密钥(Secrets)

API Key 不要写死在代码里,用 Wrangler 管理:

# 设置密钥(交互式输入,不会显示在屏幕上)
<REDACTED_TOKEN>

# 查看已设置的密钥
wrangler secret list

代码里通过 env.API_KEY 访问:

interface Env {
  BUCKET: R2Bucket;
  API_KEY: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const auth = request.headers.get("Authorization");
    if (auth !== `Bearer ${env.API_KEY}`) {
      return new Response("Unauthorized", { status: 401 });
    }
    // ...
  },
};

绑定自定义域名

# 方法 1:在 wrangler.toml 里配置
# routes = [{ pattern = "api.example.com", custom_domain = true }]

# 方法 2:在 Cloudflare Dashboard 里配置
# Workers & Pages → 你的 Worker → Settings → Domains & Routes

前提:域名已经托管在 Cloudflare(DNS 由 CF 管理)。


常用命令速查

# === Worker ===
wrangler init                 # 初始化项目
wrangler dev                  # 本地开发
wrangler deploy               # 部署
wrangler tail                 # 实时查看日志
wrangler delete               # 删除 Worker

# === R2 ===
wrangler r2 bucket create <name>   # 创建存储桶
wrangler r2 bucket list            # 列出存储桶
wrangler r2 bucket delete <name>   # 删除存储桶
wrangler r2 object put <bucket>/<key> --file=<path>  # 上传文件
wrangler r2 object get <bucket>/<key>                # 下载文件
wrangler r2 object delete <bucket>/<key>             # 删除文件

# === Secrets ===
wrangler secret put <name>    # 设置密钥
wrangler secret list          # 列出密钥
wrangler secret delete <name> # 删除密钥

# === 调试 ===
wrangler whoami               # 查看登录账户
wrangler login                # 重新登录

我踩过的坑

1. wrangler dev 默认不绑定 R2

本地开发时访问 env.BUCKET 会报错。解决:

wrangler dev --remote

这样会用真实的 R2,但请求数会计入配额。

2. R2 的 body 是流,不能直接用两次

// 错误:body 只能读一次
const text1 = await object.text();
const text2 = await object.text();  // 报错!

// 正确:先读成 buffer
const buffer = await object.arrayBuffer();

3. 部署后改动没生效

Cloudflare 有缓存,刷新可能看到旧版本。两个办法:

  • 等 1-2 分钟
  • 强刷:Cmd+Shift+R

实际案例

我现在跑着这些 Worker:

服务 用途 R2 存储
s.chen.rs 文章发布 文章 JSON + 索引
img.chen.rs 图床 图片文件
ai.chen.rs OpenAI Key 轮询网关 Key 配置

每个都是一个 Worker + 一个 R2 Bucket,代码不超过 500 行。


为什么 AI 能直接操作

Wrangler 是纯命令行工具,所有操作都可以非交互式完成:

# 创建项目
wrangler init my-project --yes

# 部署(不需要确认)
wrangler deploy

# 设置密钥(可以从 stdin 读取)
echo "sk-xxx" | <REDACTED_TOKEN>

这意味着你可以让 Claude Code 帮你:

  • 写 Worker 代码
  • 创建 R2 存储桶
  • 部署上线
  • 设置密钥

整个流程全自动。


下一步

  • 加 KV:需要键值存储?wrangler kv:namespace create <name>
  • 加 D1:需要 SQLite?wrangler d1 create <name>
  • 加定时任务:在 wrangler.toml 里配置 [triggers]

附录:给 AI 的复现指令

帮我用 Cloudflare Worker + R2 搭建一个简单的图床服务。

目标:POST 上传图片,返回公开 URL;GET 访问图片。

技术栈:TypeScript + Wrangler

项目结构:
img-worker/
├── src/index.ts      # 主入口
├── wrangler.toml     # 配置
└── package.json

wrangler.toml 配置:
name = "img-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"

<span class="private-link" title="未发布的笔记">r2_buckets</span>
binding = "BUCKET"
bucket_name = "images"

核心逻辑:
1. POST /upload:验证 Authorization header,读取文件,生成哈希作为文件名,存入 R2,返回 URL
2. GET /:key:从 R2 读取文件,设置正确的 Content-Type,返回

环境变量:
- API_KEY: 上传认证密钥(通过 <REDACTED_TOKEN> 设置)

部署命令:
wrangler r2 bucket create images
<REDACTED_TOKEN>
wrangler deploy

成功标志:
1. curl POST 上传图片返回 URL
2. 浏览器访问 URL 能看到图片

附录:定价

服务 免费额度 超出后
Worker 请求 10 万/天 $0.50/百万
R2 存储 10 GB $0.015/GB/月
R2 A 类操作 100 万/月 $4.50/百万
R2 B 类操作 1000 万/月 $0.36/百万
R2 出口流量 无限 $0

个人项目基本用不完免费额度。