
https://www.imagetinys.com/notes/rclone-operation-manual-r2-file-upload
Cloudflare R2 是一个兼容 S3 的对象存储服务,最大的亮点是零出站流量费。对于作品集网站这种需要大量图片和视频的场景,传统对象存储(S3、OSS)的出站费用会随访问量增长,R2 则完全免除了这笔开支。
项目:存储
免费额度(每月):10 GB
超出后价格:$0.015 / GB
项目:Class A 操作(写入:PUT、POST、LIST)
免费额度(每月):100 万次
超出后价格:$4.50 / 百万次
项目:Class B 操作(读取:GET)
免费额度(每月):1000 万次
超出后价格:$0.36 / 百万次
项目:出站流量
免费额度(每月):无限
超出后价格:$0(永久免费)
💡 Class A 和 Class B 是什么?
Class A(写入类)= 改变数据的操作,如上传文件(PUT)、列出文件(LIST)、删除文件等。对应 rclone 的 copy、sync、ls、delete 命令。
Class B(读取类)= 只读取数据的操作,主要是下载 / 访问文件(GET)。对应实际场景就是访客每次在浏览器加载你的图片或视频。作品集网站访客看得多、上传少,所以 Class B 免费额度给了 1000 万次。
个人作品集网站的规模,10 GB 存储基本够用。即使超出,存储费用也很低,而最贵的出站流量永远免费。
我的作品集网站用 Notion 作为 CMS,之前所有媒体文件(视频、封面图、幕后截图)都直接存在 Notion 数据库里。实际使用中遇到了不少问题:
URL 过期:Notion API 返回的媒体 URL 约 1 小时过期,前端必须做缓存 + 预热来维持可用性,逻辑复杂且不稳定
请求变慢:媒体文件多了以后,Notion API 的响应速度明显下降,尤其是一次查询多条带文件属性的记录时,经常要等好几秒
文件大小受限:Notion 单个文件上传限制 5 MB(免费版)/ 5 GB(付费版),大视频需要额外压缩或拆分
没有 CDN 加速:Notion 的文件托管没有全球 CDN,访客加载速度不理想
不可控:文件存在 Notion 内部,无法自定义域名、无法批量管理、无法接入自己的缓存策略
将静态资源迁移到 R2 后,Notion 数据库只保留纯文字字段(slug、标题、标签等),前端通过 slug 拼接 R2 的永久 URL,不再存储任何媒体链接。URL 永久稳定、成本低、可控性强。
日常上传文件之前是用 Cloudflare 网页后台手动拖拽,每次发布作品都要登录 Dashboard、找目录、拖文件,慢且容易出错。换成 rclone 命令行后,一行命令就能上传整个目录,配合脚本可以做到双击即用。
💡 ⚠️请替换成你自己的路径! 本文以我的 R2 存储桶 tinys-website 和 CDN 域名 cdn.imagetinys.com 为例,你需要替换为自己的:
tinys-website → 你的存储桶名
cdn.imagetinys.com → 你的 CDN 域名
work/ → 你的实际目录结构
winget install Rclone.Rclone
安装完成后重启终端,验证:
rclone version
💡 如果 winget 不可用,直接去 https://rclone.org/downloads/ 下载 zip,解压后把 rclone.exe 所在目录加到系统 PATH。
打开 Cloudflare Dashboard → R2 对象存储
右侧 Account Details 区域,点击 API Tokens 旁的 Manage
点击 创建 User API 令牌
权限选 对象读和写(日常上传够用,不需要管理员权限)
指定存储桶可选,存储桶和 TTL 保持默认即可
点击 创建 User API 令牌 确认
创建成功页面会显示三个值,当场复制保存(离开后不再显示):
访问密钥 ID(Access Key ID)
机密访问密钥(Secret Access Key)
S3 端点(Endpoint),格式为 https://.r2.cloudflarestorage.com
⚠️ Secret Access Key 只显示一次,如果没存就只能删掉令牌重新创建。
rclone config
按提示操作:
提示:e/n/d/r/c/s/q>
输入:n
说明:新建远程
提示:name>
输入:r2
说明:远程名称,后续命令用 r2: 引用
提示:Storage>
输入:s3
说明:Amazon S3 Compliant
提示:provider>
输入:Cloudflare
说明:选 Cloudflare R2
提示:env_auth>
输入:1
说明:false,手动输入凭证
提示:access_key_id>
输入:粘贴你的 Access Key ID
说明:从 R2 令牌创建页面复制
提示:secret_access_key>
输入:粘贴你的 Secret Access Key
说明:从 R2 令牌创建页面复制
提示:region>
输入:直接回车
说明:R2 自动分配区域
提示:endpoint>
输入:粘贴你的 S3 端点
说明:格式 https://.r2.cloudflarestorage.com
提示:Edit advanced config?
输入:n
说明:不需要高级配置
提示:Keep this "r2" remote?
输入:y
说明:确认保存
提示:e/n/d/r/c/s/q>
输入:q
说明:退出配置
# 列出 work/ 下的文件(对象读写权限不支持 lsd 列桶,直接列文件即可)
rclone ls r2:tinys-website/work/
能看到文件列表就说明连接成功。
rclone copy ./digital-flower-lycoris r2:tinys-website/work/digital-flower-lycoris -P
💡 路径说明:./digital-flower-lycoris 是相对路径,指你当前终端所在目录下的 digital-flower-lycoris 文件夹。执行前先 cd 到文件夹的父目录,或者直接用绝对路径:
rclone copy D:\projects\digital-flower-lycoris r2:tinys-website/work/digital-flower-lycoris -P
左边 = 本地路径(文件夹或文件),右边 = R2 目标路径。-P 显示上传进度。
记得把 r2:tinys-website/work/ 替换成你自己的存储桶和目录。
本地目录结构:
digital-flower-lycoris/
├── cover-01.webp
├── cover-02.webp
├── 01.mp4
└── breakdown.webp
上传后 R2 路径:
work/digital-flower-lycoris/
├── cover-01.webp
├── cover-02.webp
├── 01.mp4
└── breakdown.webp
rclone copy ./cover-01.webp r2:tinys-website/work/digital-flower-lycoris/
# 列出某个作品的所有文件
rclone ls r2:tinys-website/work/digital-flower-lycoris/
# 列出所有作品目录
rclone lsd r2:tinys-website/work/
# 删除单个文件
rclone delete r2:tinys-website/work/old-project/cover-01.webp
# 删除整个作品目录
rclone purge r2:tinys-website/work/old-project
# copy 默认只上传新增/修改的文件,不会删除远端多余文件
rclone copy ./digital-flower-lycoris r2:tinys-website/work/digital-flower-lycoris
# 如果要让远端完全等于本地(删除远端多余文件),用 sync
rclone sync ./digital-flower-lycoris r2:tinys-website/work/digital-flower-lycoris
⚠️ sync 会删除远端多余文件,谨慎使用。日常用 copy 就够了。
每次要cd到路径或者输入绝对路径看起来是不是有点麻烦?我也觉得,所以我让AI帮我写了两个常用的脚本👇
把脚本复制到作品文件夹内,双击运行即可。脚本自动读取文件夹名作为 slug,上传时排除脚本自身。
打开记事本(按 Win + R,输入 notepad,回车)
把下面对应的脚本代码复制粘贴进去
点击 文件 → 另存为
“保存类型”选 所有文件 (.*)(不要选“文本文件”,否则会变成 .bat.txt)
文件名输入 r2-upload work-copy.bat(或 r2-upload work-sync.bat)
编码选 UTF-8
保存到你常用的位置(如桌面,我是有个专门放脚本工具的文件夹),以后复制到作品文件夹即可使用
💡 两个脚本只需创建一次,以后每次复制到新的作品文件夹里直接双击用。
只上传新增/修改的文件,不删除远端多余文件。日常用这个。
@echo off
chcp 65001 >nul
cd /d "%~dp0"
for %%I in ("%~dp0.") do set "folder=%%~nxI"
rem ↓↓↓ 把 tinys-website/work 替换成你自己的存储桶和目录 ↓↓↓
set "target=r2:tinys-website/work/%folder%"
echo [COPY] Local: %folder%
echo [COPY] Target: %target%
echo.
rclone copy . %target% --exclude "*.bat" --exclude "*.ps1" -P
echo.
echo --- Verifying upload ---
rclone ls %target%
echo.
echo Upload complete!
pause
让远端完全等于本地,会删除远端多余文件。执行前会要求确认。
@echo off
chcp 65001 >nul
cd /d "%~dp0"
for %%I in ("%~dp0.") do set "folder=%%~nxI"
rem ↓↓↓ 把 tinys-website/work 替换成你自己的存储桶和目录 ↓↓↓
set "target=r2:tinys-website/work/%folder%"
echo [SYNC] Local: %folder%
echo [SYNC] Target: %target%
echo [WARN] Remote files not in local will be DELETED!
echo.
set /p confirm=Confirm sync? (y/n):
if /i "%confirm%"=="y" (
rclone sync . %target% --exclude "*.bat" --exclude "*.ps1" -P
echo.
echo --- Verifying upload ---
rclone ls %target%
echo.
echo Sync complete!
) else (
echo Cancelled.
)
pause

发布一个新作品的完整步骤:
创建一个文件夹,命名为 slug(如 digital-flower-lycoris)
压缩视频、转换封面图为 webp,放进文件夹
把 r2-upload work-copy.bat 复制到文件夹里
最终文件夹结构:
digital-flower-lycoris/
├── cover-01.webp
├── cover-02.webp
├── 01.mp4
├── breakdown.webp
└── r2-upload work-copy.bat
双击 r2-upload work-copy.bat,脚本自动上传并验证。
https://cdn.imagetinys.com/work/digital-flower-lycoris/cover-01.webp
重启终端,或手动把 rclone 路径加到 PATH:
# PowerShell 临时添加
$env:PATH += ";C:\path\to\rclone"
检查路径是否正确:
# 确认文件在 work/ 下而不是根目录
rclone ls r2:tinys-website/work/your-slug/
Cloudflare 有缓存,手动清除:
Cloudflare Dashboard → Caching → Purge Cache → Custom Purge
填入需要刷新的 URL,如:
https://cdn.imagetinys.com/work/digital-flower-lycoris/cover-01.webp
rclone copy ./digital-flower-lycoris r2:tinys-website/work/digital-flower-lycoris -P
-P 参数显示实时进度条。
-