多账号 Cloudflare R2 聚合图床 · 上传 / 直链 / 管理 · 统一短链 · 轮询分发
新号接入:Cloudflare 侧设置(6 步)
| 步骤 | 操作 | 入口 | 不做会怎样 |
|---|---|---|---|
| 1 | 开通 R2 计划(免费) | 控制台左侧 R2 | 无法建桶 |
| 2 | 绑定支付方式(信用卡/PayPal) | 结算 → 付款方式 | R2 用不了(免费额度内不扣费但必须绑卡) |
| 3 | 创建存储桶 | R2 → 概述 → 创建存储桶 | — |
| 4 | 开启公共访问 | 桶 → 设置 → 公共访问 → 允许访问(r2.dev) | 链接全部 404!最常漏的一步 |
| 5 | 创建 API 令牌(对象读和写) | R2 → 管理 R2 API 令牌 | 平台无法上传 |
| 6 | 复制 Account ID + AK + Secret 到本平台 | Secret 只显示一次 | — |
📌 建议顺手做:每个账号绑卡后在「结算 → 通知」加预算告警(如 $0.5),超额立刻收邮件;开启两步验证防封号。
💡 批量接入用控制台「批量导入」行格式:
💡 批量接入用控制台「批量导入」行格式:
名称|AccountID|AK|SK|桶名|公开域名 一行一号。❓ 这些参数怎么获取?
全程在 Cloudflare 控制台操作:dash.cloudflare.com ↗
① Account ID(账号 ID)
登录 Cloudflare 控制台 → 左侧「R2 对象存储」→ 概述页右侧「帐户详情」面板 → 复制帐户 ID。填入后 endpoint 自动拼好,不用手动填。
② Access Key ID / Secret Access Key
R2 概述页 → 右上角「管理 R2 API 令牌」→「创建 API 令牌」→ 权限选对象读和写,范围选「仅应用于特定桶」并勾选你的桶 → 创建后显示两串值。Secret 只显示这一次,立即复制保存!
③ bucket(R2 桶名)
R2 →「概述」→ 创建存储桶时起的名字。一个账号可建多个桶,每个桶在本平台算一条记录。
④ 公开访问域名 public_base_url(推荐配置)
进入你的桶 →「设置」→「公共访问」:方式 A 点允许访问开启 r2.dev 子域,复制形如
https://pub-xxxx.r2.dev;方式 B 绑定自定义域名(需域名托管在同一 CF 账号)。不开公共访问 = 链接全部 404!⑤ 名称 label / 自定义 endpoint / Path Style
label 是自己起的唯一代号(acc01、acc02…),用于轮询分发和短链展示;endpoint 留空自动按 Account ID 拼 R2 地址(只有接 MinIO 才填);Path Style 保持默认「是」。
鉴权方式
除标注 公开 的接口外,所有请求需带请求头:
Authorization: Bearer <你的密钥>
| 密钥类型 | 获取方式 | 权限 |
|---|---|---|
API 密钥r2h_xxx... | 控制台 →「API 密钥」→ 新建(完整值只显示一次) | 上传 / 预签名 / 确认,推荐程序使用 |
| 管理员 Token | 部署时 ADMIN_TOKEN 环境变量 | 全部接口(含桶管理、巡检) |
接口总览
| 接口 | 鉴权 | 说明 |
|---|---|---|
GET/health | 公开 | 健康检查 |
POST/upload | API Key | multipart 上传单张图片 |
POST/api/presign | API Key | 获取预签名直传地址(推荐) |
PUT{upload_url} | 签名自带 | 直传字节到 Cloudflare |
POST/api/presign/confirm | API Key | 可选:回报真实大小 |
GET/b/{key} | 公开 | 统一短链,302 跳转或代理输出 |
GET/f/{label}/{key} | 公开 | 指定桶的代理下载 |
GET/api/images | 管理员 | 上传记录分页列表 |
DEL/api/images/{id} | 管理员 | 删除记录并删除云端对象 |
POST/api/buckets/bulk | 管理员 | 批量导入桶配置(JSON 数组) |
POST/api/buckets/check_all | 管理员 | 手动触发全量巡检,死号自动停用 |
GET/api/keys 等 | 管理员 | API 密钥增删改查(见控制台) |
方式一:直接上传(简单)
curl -X POST http://你的地址:8100/upload \
-H "Authorization: Bearer r2h_你的密钥" \
-F "file=@cat.jpg"
响应:
{
"id": 42,
"url": "https://pub-xxxx.r2.dev/images/20260825/18f3a1b2c3.jpg",
"short_url": "http://你的地址:8100/b/images/20260825/18f3a1b2c3.jpg",
"proxied_url": "http://你的地址:8100/f/account01/images/20260825/18f3a1b2c3.jpg",
"bucket": "account01",
"key": "images/20260825/18f3a1b2c3.jpg",
"size": 102400,
"filename": "cat.jpg"
}
💡 不指定桶时自动在所有启用且未满的桶之间轮询分发;某号临时故障自动换下一个重试。也可
?bucket=acc01 强制指定。方式二:预签名直传(推荐,省 VPS 流量)
图片字节不经过服务器,直接从你的程序进 Cloudflare。
import requests
BASE = "http://你的地址:8100"
H = {"Authorization": "Bearer r2h_你的密钥"}
# 第1步:要签名地址
p = requests.post(f"{BASE}/api/presign", headers=H,
json={"filename": "gen.png", "size": len(img)}).json()
# 第2步:直传 Cloudflare(签名 15 分钟内有效)
requests.put(p["upload_url"], data=img,
headers={"Content-Type": "image/png"})
# 第3步:拿到永久链接
print(p["short_url"]) # 统一短链
print(p["url"]) # 最终直链
| presign 请求字段 | 必填 | 说明 |
|---|---|---|
| filename | 否 | 原始文件名,决定扩展名 |
| size | 建议传 | 文件字节数,用于容量配额预判 |
| content_type | 否 | 如 image/png;传了则 PUT 必须带相同 Content-Type 头 |
| bucket | 否 | 强制指定桶 label;不传走轮询 |
⚠️ 签名 URL 有效期 900 秒,只能对返回的 key 做一次 PUT。
confirm 可选:POST /api/presign/confirm {"key":"...","size":12345}图片链接说明
| 字段 | 形式 | 适用 |
|---|---|---|
| short_url | /b/{key},302 跳转 | 对外分享首选:统一域名、隐藏真实桶、带浏览器缓存 |
| url | 桶的公开域名直链 | 已知并信任该桶时少一跳 |
| proxied_url | /f/{桶}/{key} | 桶未绑定公开域名时的代理通道 |
错误码
| 状态码 | 含义 |
|---|---|
| 400 | 参数缺失 / 没有可用桶 |
| 401 | 密钥无效或被停用 |
| 413 | 超过 MAX_UPLOAD_MB 上限 |
| 502 | 所有候选桶均失败 / 签名生成失败(详情见 detail 字段) |
PicGo 自定义图床配置
| 项 | 值 |
|---|---|
| API 地址 | http://你的地址:8100/upload |
| POST 参数名 | file |
| JSON 路径 | url(想要短链就填 short_url) |
| 自定义请求头 | {"Authorization": "Bearer r2h_你的密钥"} |
R2 Hub v1.1 · 单用户多账号 R2 聚合图床 · 轮询分发 / 故障切换 / 容量配额 / 自动巡检