抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

相信绝大多数Blogger都有使用对象存储的需求,国内诸如阿里云腾讯云七牛云华为云等多个厂家也都提供了自家的图床。

博主之前也曾使用过七牛云的存储服务,虽然使用七牛云一年以来,所支付的费用也不算多,但收费规则对我来说还是模糊的,继续使用下去的意愿也不大了其实是懒得算账

那么有没有完全免费的图床可以用呢?之前也使用过jsDelivr+Github的组合拳,但jsDelivr一直被干扰,实际上在大陆体验效果一般,况且用作图床Github仓库也必须是公开的,也不便于管理。

Backblaze为什么是神?

在把整个博客部署到Cloudflare Pages之后,机缘之下,我发现了BackBlaze提供的存储服务,其B2云存储提供10GB的免费空间,10GB对我来说是很奢侈了,而且每天也有1GB的免费下载流量,个人博客而言完全够用。

即使你使用的存储额度超出了10GB,超过的部分每月也只按0.005USD/GB的价格收费,下载量超过1GB也仅仅只需0.01USD/GB。

0.005USD≈0.036RMB

那么为什么我要提到Cloudflare呢?那是因为Backblaze与Cloudflare一样是带宽联盟(Bandwidth Alliance)的合作伙伴,使用Cloudflare提供的CDN服务访问Backblaze的资源是不存在数据传输费用的。如此,你就能够拥有一个近乎免费的云存储空间。

Backblaze的优点:

  • 便宜量大管饱
  • API丰富,有成熟的讨论社区
  • 资源上传方式多样
  • 不限制存储文件类型,你甚至可以用它当云盘(我没这么干过)

Backblaze的缺点:

  • 云存储服务架设在国外,国内访问存在风险
  • 学习成本略高,官网中文翻译一坨

Cloudflare又是什么高手了?

Cloudflare 的主要产品和服务包括内容传递网络(CDN)、DDoS 攻击防护、DNS(域名系统)服务、防火墙、安全服务和分布式存储等。该公司通过其全球分布的数据中心网络,将用户的网站流量路由到离用户最近的服务器,从而加速网站加载速度,提高性能,并保护网站免受各种网络攻击的侵害。

Cloudflare Pages提供免费的静态博客部署服务(如你所见,我的博客就是通过这个服务搭建而来),Cloudflare Workers提供类似Github Actions的免费无服务器应用程序。

All you need:

  • Backblaze账号
  • Cloudflare账号
  • 你自己的域名
  • 耐心

Backblaze创建存储桶

进入官网,点击Get Started,选择B2 Cloud Storage中的Try for Free,登录账号,点击Create a Bucket创建存储桶,你会看到如下界面:

注意,此处的Bucket应设置为Private,因为不知从什么时候起,创建Public的桶就需要在Backblaze上绑定信用卡并有消费记录或是一次性支付1USD作为创建这个桶所需要的价格,那显然是不符合能省钱就省钱的价值观的。并且Public的桶存在被人恶意刷流量的可能,所以理论上还是Private的桶更保险一些,至于Default EncryptionObject Lock保持默认即可。

Bucket Unique Name随意,建议设置得复杂一些。

PS:建议关闭不知所云的中文翻译

选做:打开创建的存储桶的Bucket Settings,在Bucket Info中添加{“cache-control”:”max-age=43200”}

之后我们点击Upload/Download图标上传一张图片,在Browse Files里点开存有图片的桶,随机点开一张,查看详细信息,我们需要使用到的访问图片的链接是Friendly URL

Friendly URL有其固定的结构,假设一张图片的Friendly URLf002.backblazeb2.com/file/any-bucket-name/avatar.png,那么这张图片的URL可分为:

  • 主机名(记住这个):f002.backblazeb2.com
  • 文件后缀:file
  • 存储桶名称:any-bucket-name
  • 图片的路径:avatar.png

到这里,我们的工作转向Cloudflare


使用Cloudflare托管你的域名

为了能够将你的域名托管到Cloudflare上,你需要将你自己的域名的NS修改为Cloudflare提供的NS,同时将所有DNS解析都转移到Cloudflare上,网上有许多优秀详细的教程,请根据你自己的域名提供商寻找相应的解决方法。

之后,在Cloudflare里添加一条CNAME记录,Name填上你的二级域名,Target填上Friendly URL中的主机名。

这样就相当于,你用自己托管的域名(假设为yourdomain.site)设置了一个二级域名file.yourdomain.site,将其指向B2主机名f002.backblazeb2.com

那么原本的图片URLf002.backblazeb2.com/file/any-bucket-name/avatar.png就可以用file.yourdomain.site/file/any-bucket-name/avatar.png访问,此时我们可以一试,会发现没有Token无法访问,报错信息如下:

1
2
3
4
5
{
"code": "unauthorized",
"message": "",
"status": 401
}

不过更常见的状况应该是Error 522:Connection timed out

为什么呢?默认情况下的Cloudflare通过纯HTTP访问上游服务器,而Backblaze仅支持HTTPS连接,所以Cloudflare发出的HTTP请求失败。

为了解决这一问题,将Cloudflare仪表板中的SSL/TLS中的加密模式改为Full (strict):

接下来,我们来优化访问图片的URL


创建规则重写访问图片URL

单击Cloudflare仪表板左侧的Rules,然后点击进入Transform Rules,如果你使用Cloudflare的免费计划,那么你一共有10条免费的规则可以设置(对于此教程,我们只需要两条规则)

Rewrite URL下创建一条规则,首先看到When incoming requests match…部分,设置如下图:

我们希望能够直接以二级域名+图片路径的方式访问图片,而不是需要额外添加/file/<桶名称>来访问,为了达成这一目的,我们需要在Then中设置Path,具体设置如下图:

这个表达式相当于当你在使用file.yourdomain.site/xxx.jpg访问图片时,会自动拼接成file.yourdomain.site/file/<你的桶名>/xxx.jpg,这一过程并不会显式地改变你访问的URL,相当于隐藏了你的桶名。

由于我们的桶是Private的,我们需要一个Token来访问它,所以在Query这里我们需要额外设置:

Authorization=后的值可以瞎填,后面再处理。


修改http响应头

接下来我们需要优化响应头,提高性能,将Transform Rules切换到Modify Response Header,添加一条规则,If...配置如下图:

Then的设置比较多,如下表:

Header name Value
Set static Access-Control-Allow-Origin *
Set dynamic ETag concat(http.response.headers[“x-bz-content-sha1”][0], http.response.headers[“x-bz-info-src_last_modified_millis”][0], http.response.headers[“x-bz-file-id”][0])
Set static cache-control public, max-age=31536000
Remove x-bz-content-sha1
Remove x-bz-file-id
Remove x-bz-file-name
Remove x-bz-info-s3b-last-modified
Remove x-bz-info-sha256
Remove x-bz-info-src_last_modified_millis
Remove x-bz-upload-timestamp

最后的结果:


使用Workers自动更新Token

我们回到Backblaze账户,申请一个Application Keys,如下图:

Allow acces to Bucket(s)选择你的桶名,勾选Read and Write

将生成的keyIDapplicationKey保存好,回到Cloudflare。

创建一个Worker,在Edit Code中填入如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
const config = {
// B2设置,填入你自己的配置
B2KeyID: "",
B2AppKey: "",
B2BucketName: "",
// Cloudflare config
cfDomain: "",
cfAuthEmail: "",
cfAuthKey: "",
cfZoneID: "",
cfRulesetID: "",
cfRuleID: "",
};

addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});
addEventListener("scheduled", (event) => {
event.waitUntil(updateRule(config));
});

const getB2Token = async (config) => {
const { B2KeyID, B2AppKey } = config;
const res = await fetch(
"https://api.backblazeb2.com/b2api/v2/b2_authorize_account",
{
headers: {
Authorization: "Basic " + btoa(B2KeyID + ":" + B2AppKey),
},
}
);
const data = await res.json();
return data.authorizationToken;
};

const updateRule = async (config) => {
const b2Token = await getB2Token(config);
const {
B2BucketName,
cfDomain,
cfAuthEmail,
cfAuthKey,
cfZoneID,
cfRulesetID,
cfRuleID,
} = config;

const res = await fetch(
`https://api.cloudflare.com/client/v4/zones/${cfZoneID}/rulesets/${cfRulesetID}/rules/${cfRuleID}`,
{
method: "PATCH",
headers: {
"X-Auth-Email": cfAuthEmail,
"X-Auth-Key": cfAuthKey,
},
body: `{
"description": "B2 ${B2BucketName}",
"action": "rewrite",
"expression": "(http.host eq \\\"${cfDomain}\\\")",
"action_parameters": {
"uri": {
"path": {
"expression": "concat(\\\"/file/${B2BucketName}\\\", http.request.uri.path)"
},
"query": {
"value": "Authorization=${b2Token}"
}
}
}
}`,
}
);

const data = await res.text();
console.log(data);
return data;
};

async function handleRequest(request) {
const data = await updateRule(config);
return new Response(data);
}
变量名 内容
B2Key keyID
B2AppKey applicationKey
B2BucketName 桶名
cfDomain 你的访问域名
cfAuthEmail Cloudflare账号邮箱
cfAuthKey Cloudflare的Application Key
cfZoneID 你的域名的区域ID
cfRulesetID Cloudflare的规则集ID
cfRuleID Cloudflare的规则ID

cfAuthKey获取方法:点击右上人像,在My Profile中的API Token页,点击Global API Key中的View

cfZoneID获取方法:点击进入你托管的域名,在OverView页面最右边栏中的API处。

cfRulesetIDcfRuleID获取方法:

打开左边栏的Manage Account->Audit log,找到Rulesets update,点开之后按下图寻找:

如果没找着直接更新一下规则名称就出现了。

以上配置完成之后,你应该可以直接通过file.youdomain.site/xxx.jpg访问到你想要的图片了。

你还可以设置TriggersWorkers每隔一段时间自动运行,我设置的是每4小时运行一次:


后记

至此你就拥有了一个免费的文件存储服务,而如何方便快捷地上传图片,请自行查找相关教程,此处不予介绍。

评论