基于CloudflareR2搭建博客图床
大概是去年这个时候,将博客托管到了cloudflare上面,详情移步博客v0.9迭代记录这篇博客。
在过去几年中,一直使用七牛的存储作为自己的博客图床。,由于去年迁移博客主站的时候,自己的云服务器还没有到期,域名备案还在,就没有处理博客文章中图片对应的图床服务。
最近突然收到了域名冻结的邮件,才想起域名备案被取消了,导致之前解析到七牛的cdn域名也被冻结无法访问了。
带来的直接影响就是之前博客中的图片链接都失效了,于是趁着周末研究了一下新的图床服务,最后决定同样使用coludflare的R2存储服务搭建自己的博客图床,最后成功完成了迁移。本文记录一下整个过程
参考
申请R2服务
cloudflare R2每个月有免费10个G的存储,且不用备案,非常方便,非常适合博客自用图床、免备案的场景使用。
之前七牛每个月需要支付几毛钱的费用,我的博客站点流量访问并不高,日均60~200左右的pv,因此10G的免费额度应该是足够的。
迁移过来的好处就是不用再关心cdn域名的ssl证书到期和备案的问题了。
填写信用卡
需要添加一个支付方式,可以直接用国内银行的信用卡
个人用没问题,免费额度基本够用
创建bucket
R2开通之后,在创建一个bucket存储桶即可
然后,可以在网页里面上传一个测试文件验证,上传后的文件可以直接在bucket主页查看
文件数量过多的话,可以使用命令行工具本地同步,具体参考下面的“同步文件到R2”章节
绑定域名
文件上传之后,还需要一个公网域名来访问,在设置-自定义域这里绑定一个即可
由于我希望可以无缝迁移到R2,这里的无缝迁移指的是在使用R2托管的图床资源内容后,以前的博客文章里面的文章链接,完全不用修改,可以直接访问到新存储桶的图片资源。
由于之前用的是img.shymean.com
这个域名,因此将这个域名域名现在解析到当前bucket的自定义域就行,等待解析完成之后,就可以用这个域名访问bucket里面的资源了
由于我的主域名shymean.com
之前就已经绑定到了Cloudflare上面,添加子域名来作为这个bucket的自定义域非常简单,只需要填写完成,然后cloudflare会自动解析。这一步大概需要几分钟的时间。
域名解析成功之后,点进刚才上传的测试文件,可以看到下面的URL访问地址,已经变成了自定义域的内容
在浏览器地址栏输入这个地址,可以看到对应的内容,就说明整个流程跑通了。
剩下只需要我将之前的存储文件迁移到新的R2存储即可。
同步文件到R2
参考:cloudflare提供的克隆文档克隆文档
R2访问密钥
首先创建API访问密钥,R2的API除了基础的accessKeyId
和secretAccessKey
之外,还有一个endpoint
的参数,后续使用aws-sdk
本地上传单个文件的时候,也需要这个endpoint
参数
注意创建的秘钥权限,应该需要包含读写功能,不然有些功能会收到权限限制,主要保证秘钥不泄露出去就行
安装配置rclone
rclone (rsync for cloud storage
)是一个开源的命令行程序,用于与不同的云存储提供商同步文件和目录。
首先安装命令工具,可以参考rclone Install 官方文档
mac直接用brew安装就行了,这个安装需要等一会才能完成
brew install rclone
安装完成后执行rclone config file
,首次安装完成的话没有config文件,会提示你去~/.config/rclone/rclone.conf
创建一个就行
touch ~/.config/rclone/rclone.conf
然后在配置文件中配置一下r2的相关参数,这个在上面的克隆文档地址里面都有
[r2]
type = s3
provider = Cloudflare
# 这里就是配置上面那一步获取到的秘钥
access_key_id = abc123
secret_access_key = xyz456
endpoint = https://<accountid>.r2.cloudflarestorage.com
acl = private
下载七牛上面的历史存储内容
由于coludflare R2并没有提供直接从七牛迁移到R2的工具,需要手动迁移。
过去几年写的博客也不是很多,传到图床上的图片数量也比较少,手动同步花不了多久时间,因此就决定手动下载到本地,再通过rclone同步到R2。
在这个过程中,碰到了一个比较尴尬的事情:由于备案过期了,七牛将之前解析的cdn域名给冻结了,七牛有一个产品限制:没有绑定外链的bucket,无法在网页端进行预览和下载。
看到这里我心里咯噔一下,那不是之前的文件都没法拿到了?
幸好之前下载过七牛的Kodo broswer
,打开这个客户端工具,进入对应的bucket,发现可以全选文件和目录,然后点击下载,文件终于排队进入下载了(悬着的心着地了...
文件能够下载到本地,剩下的就是同步到R2上就行了。
同步命令
虽然在云存储中,并没有文件夹的概念,只是路径展示上可能包含多个path而已
但是通过Kodo下载下来的文件保持了原本的目录结构,这比较省事,避免了我还需要写个脚本来转成本地的目录文件结构。
为什么目录路径比较重要呢?
由于我希望迁移过去的链接可以与之前文章中的结构保持一致,这样的话,历史失效的 ${host}/xxx/xx.webp
的路径,只要切换域名指向后,就可以恢复访问,不用逐个修改之前的文章。
文件夹下载下来之后,只需要通过rclone将这个文件夹同步到上面创建的R2 bucket就行了。
同步本地文件夹到R2的对应bucket中,可以使用rclone sync
命名,文件数量较多的话,可以添加-P
参数显示进度。
需要注意:该命令会移除那些在远程bucket但是不在本地目录中的文件,因此一般仅作为初始化的命令,避免后续通过其他方式添加到这个bucket里面的文件被sync的时候给移除了。
rclone sync local_folder r2:bucket_name -P
等待一段时间,所有文件都同步上去,刷新一下网页端的bucket展示,就可以看到对应的文件都同步过来了。
再去查看之前的博客文章,失效图片的链接都恢复访问了,完美!
oPic更新
我之前参考iPic
(要收费),写了一个上传文件到图床的工具oPic,方便在写markdown博客的时候快速插入cdn图片。
实际上是可以用开源的picGo
的,picGo内置支持配置R2的图床,但是自己的oPic用习惯了,里面有一些很定制的功能,比如图片压缩、webp转换、自动拼接域名等,所以还是决定扩展一下这个小工具。
由于oPic之前只支持了七牛的配置,现在需要扩展对于R2的支持,这个还是比较简单的,在上传模块扩展一个r2的配置,接收对应的filePath本地路径,返回上传后的url即可。
现在有了AI,写代码方便多了,都不用看文档,直接就生成了可以直接使用的代码。下面是使用claude 4.0生成的使用aws-sdk
将文件上传到R2的代码
const AWS = require('aws-sdk');
const createUploadR2 = (opts) => {
const { accessKeyId, secretAccessKey, endpoint, bucket, host } = opts;
// 配置 AWS SDK 以连接到 Cloudflare R2
const s3 = new AWS.S3({
accessKeyId,
secretAccessKey,
endpoint,
s3ForcePathStyle: true, // 强制使用路径样式的 URL
signatureVersion: 'v4',
});
return (key, filePath) => {
const fs = require('fs');
return new Promise((resolve, reject) => {
// 读取文件
const fileStream = fs.createReadStream(filePath);
const uploadParams = {
Bucket: bucket,
Key: key,
Body: fileStream,
ContentType: getContentType(key), // 根据文件扩展名设置 Content-Type
};
s3.upload(uploadParams, (err, data) => {
if (err) {
reject(err);
} else {
// 返回文件的公共 URL
// 如果配置了自定义 host,使用 host + key 的格式
// 否则使用 data.Location 或默认的 endpoint 格式
let publicUrl;
if (host) {
publicUrl = host.endsWith('/') ? `${host}${key}` : `${host}/${key}`;
} else {
publicUrl = data.Location || `${endpoint}/${bucket}/${key}`;
}
resolve(publicUrl);
}
});
});
};
};
// 根据文件扩展名获取 Content-Type
function getContentType(filename) {
const ext = filename.split('.').pop().toLowerCase();
const mimeTypes = {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'webp': 'image/webp',
'svg': 'image/svg+xml',
'bmp': 'image/bmp',
'ico': 'image/x-icon',
};
return mimeTypes[ext] || 'application/octet-stream';
}
module.exports = createUploadR2;
开发测试完毕后,重新构建了一个mac端应用,现在,我又可以愉快地在写博客的时候快速插入图片了。
现在,博客站点和图床都已经托管到了cloudflare上面。
非常感谢Cloudflare,不用每年再支付一笔云服务费用,也不用每三个月再关心免费的ssl证书到期的问题,Nice啊~
PS:不过过去一年写的博客数量和质量都不太行,现在有这么好的服务,也得继续输出一些内容,反馈社区才行~
你要请我喝一杯奶茶?
版权声明:自由转载-非商用-保持署名和原文链接。
本站文章均为本人原创,参考文章我都会在文中进行声明,也请您转载时附上署名。
