侧边栏

基于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除了基础的accessKeyIdsecretAccessKey之外,还有一个endpoint的参数,后续使用aws-sdk本地上传单个文件的时候,也需要这个endpoint参数

注意创建的秘钥权限,应该需要包含读写功能,不然有些功能会收到权限限制,主要保证秘钥不泄露出去就行

安装配置rclone

rclonersync for cloud storage)是一个开源的命令行程序,用于与不同的云存储提供商同步文件和目录。

首先安装命令工具,可以参考rclone Install 官方文档

mac直接用brew安装就行了,这个安装需要等一会才能完成

brew install rclone

安装完成后执行rclone config file,首次安装完成的话没有config文件,会提示你去~/.config/rclone/rclone.conf创建一个就行

touch ~/.config/rclone/rclone.conf

然后在配置文件中配置一下r2的相关参数,这个在上面的克隆文档地址里面都有

conf
[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的时候给移除了。

bash
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的代码

js
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:不过过去一年写的博客数量和质量都不太行,现在有这么好的服务,也得继续输出一些内容,反馈社区才行~

你要请我喝一杯奶茶?

版权声明:自由转载-非商用-保持署名和原文链接。

本站文章均为本人原创,参考文章我都会在文中进行声明,也请您转载时附上署名。