使用 Cloudflare + Backblaze B2 打造一个免费的图像 CDN
微wx笑 2022-06-18【云服务】 9 0关键字: Cloudflare Backblaze B2 免费 CDN
本文是一篇详细的教程,教你怎么把图片托管在 Backblaze B2 对象存储,然后连接到 Cloudflare 的 CDN 服务。两者都有免费额度,对于小网站来说,不用花钱就解决了图片的存储和带宽问题。
转自:https://www.backblaze.com/blog/free-image-hosting-with-cloudflare-transform-rules-and-backblaze-b2/
机翻
在深入研究使用 Cloudflare 转换规则在 Backblaze B2 云存储上实现图像托管之前,我想花点时间介绍一下自己。我是 Pat Patterson,最近被 Backblaze 聘为首席开发布道者。近二十年来,我在 Sun Microsystems 和 Salesforce 等公司与技术和技术社区合作。我将为您、我们的 Backblaze B2 社区创建和提供技术内容,并代表您在 Backblaze 中进行宣传。随时关注我的旅程并通过Twitter或LinkedIn与我联系。
Cloudflare 转换规则
现在,继续表演!Cloudflare 转换规则使您可以访问CDN 边缘服务器上的 HTTP 流量,从而允许您操纵 URI 路径、查询字符串以及传入请求和传出响应的 HTTP 标头。Cloudflare Workers允许您编写在相同环境中执行的 JavaScript 代码,Transform Rules 为您提供许多相同的功能,而无需分号和花括号。
让我们看一个特定的用例:在云对象存储之上实现图像托管。Backblaze 高级用户James Ross在 2019 年 8 月写了一篇出色的博客文章,早在引入转换规则之前,解释了如何使用 Cloudflare Workers 和 Backblaze B2 做到这一点。我们将看到我们可以使用转换规则重新创建多少 James 的解决方案,而无需编写任何代码。我们还将了解 Cloudflare 和 Backblaze 的结合如何让您免费创建自己的个人 10GB 图像托管网站。
在云对象存储上实现图像托管
詹姆斯的要求很简单:
提供来自自定义域的图像文件,例如
files.example.com
,而不是云存储提供商的域。从 URL 中删除存储桶名称和任何其他无关信息。
从 HTTP 响应中删除无关的标头,例如对象 ID。
改进图像的缓存(浏览器和边缘缓存)。
添加基本的 CORS 标头以允许在外部站点上嵌入图像。
我将在这篇博文中逐一介绍这些要求,最后解释为什么 Backblaze B2 可能比其他云对象存储更适合这个和许多其他云对象存储用例的长期供应商。
值得注意的是,这里没有任何东西是 Backblaze B2 特定的——用户的浏览器通过其 URL 从B2 Cloud Storage公共存储桶请求对象,就像使用任何其他云对象存储一样。例如,这些技术在 Amazon S3 上完全相同。
先决条件
您需要同时拥有 Cloudflare 和 Backblaze 的帐户。您可以免费开始使用这两种方法:
您还需要自己的 DNS 域,我将example.com
在本文中调用它,您可以在其上创建子域,例如files.example.com
. 如果你已经读到这里,你可能已经至少有一个了。否则,您可以在 Cloudflare 注册一个新域,每年只需几美元,或您当地的等价物。
为您的图像创建一个存储桶
如果您已经有一个要用于图像存储的 B2 Cloud Storage 存储桶,则可以跳过此部分。注意:无论您是通过B2 Native API、Backblaze S3 Compatible API还是任何其他机制创建存储桶及其对象都没有关系 - Cloudflare 可以通过其友好的 URL 访问您的对象。
登录Backblaze,点击左侧B2 Cloud Storage下的Buckets,然后点击Create a Bucket。您需要给您的存储桶一个唯一的名称,并将其公开。保留其他设置的默认值。
请注意,存储桶名称在 Backblaze B2 中必须是全局唯一的,因此您不能只将其称为“myfiles”。您将从公共视图中隐藏存储桶名称,因此您可以将其命名为任何名称,只要不存在具有该名称的 Backblaze B2 存储桶即可。
最后,单击上传/下载并将测试文件上传到您的新存储桶。
单击该文件可查看其详细信息,包括其各种 URL。
在下一步中,您将使用自定义子域的请求(例如, )重写为https://files.example.com/smiley.png
表单的友好 URL,https://f004.backblazeb2.com/file/metadaddy-public/smiley.png
。
记下友好 URL中的主机名。正如您在上一段中看到的,我的是f004.backblazeb2.com
.
为您的图像主机创建 DNS 子域
如果您尚未在 Cloudflare 帐户中激活您的域(example.com
而不是),您将需要这样做。files.example.com
现在,在 Cloudflare 仪表板中,通过添加指向您之前记下的存储桶主机名的 DNS CNAME 记录来创建您的子域。
我创建了files.superpat.com
,它指向我的存储桶的主机名,f004.backblazeb2.com
.
如果您现在通过转到您的自定义子域中的测试文件的 URL 进行测试,例如,https://files.example.com/file/my-unique-bucket-name/smiley.png
几秒钟后,您将看到来自 Cloudflare 的 522“连接超时”错误:
这是因为默认情况下,Cloudflare 通过纯 HTTP 而不是 HTTPS 访问上游服务器。Backblaze 仅支持安全的 HTTPS 连接,因此 HTTP 请求失败。为了解决这个问题,在 Cloudflare 仪表板的 SSL/TLS 部分,将加密模式从“灵活”更改为“完全(严格)”,以便 Cloudflare 通过 HTTPS 连接到 Backblaze,并且需要 CA 颁发的证书。
现在您应该能够通过表单的 URL 访问您的自定义子域中的测试文件https://files.example.com/file/my-unique-bucket-name/smiley.png
。下一个任务是创建/file/my-unique-bucket-name
要从 URL 中删除的第一个转换规则。
您还必须确保在 Cloudflare 域配置中未启用自动签名交换 (SXG),因为此功能与 Backblaze B2 不兼容。有关更深入的解释,请参阅此 Cloudflare 论坛帖子。
重写传入请求的 URL 路径
Cloudflare 转换规则分为三种:
URL 重写规则:重写 HTTP 请求的 URL 路径和查询字符串。
HTTP 请求头修改规则:设置 HTTP 请求头的值或移除请求头。
HTTP 响应头修改规则:设置 HTTP 响应头的值或删除响应头。
单击Cloudflare 仪表板左侧的规则,然后单击转换规则。您会看到 Cloudflare 免费计划包括 10 条转换规则——对于我们的目的来说已经足够了。单击创建转换规则,然后单击重写 URL。
暂停片刻并思考我们需要让 Cloudflare 做什么是很有用的。用户将请求表单的 URL https://files.example.com/smiley.png
,我们希望对 Backblaze B2 的请求类似于https://f004.backblazeb2.com/file/metadaddy-public/smiley.png
。我们已经处理了 URL 的域部分,因此很明显我们需要做的就是在传出 URL 前加上/file/<bucket name>
.
为您的规则指定一个描述性名称,例如“添加文件和存储桶名称”。
有机会设置传入请求必须匹配才能触发触发器的条件。在 James 的文章中,他测试了路径尚未以/file/<bucket name>
前缀开头,因此您可以使用短 URL 或长 URL 来引用文件。
乍一看,Cloudflare 仪表板不提供“不以”作为操作员的功能。
但是,单击编辑表达式会显示一种更强大的指定条件的方法:
Cloudflare 规则语言允许我们准确地表达我们的条件:
继续前进,Cloudflare 提供了用于重写路径的静态和动态选项。静态重写将对每个请求的 URL 路径应用相同的值。此用例需要动态重写,其中,对于每个请求,Cloudflare 将值评估为生成路径的表达式。
您的表达式会在现有路径前面加上/file/<bucket name>
,如下所示:
保存转换规则,并尝试再次访问您的测试文件,这次/file/<bucket name>
在 URL 路径中没有前缀,例如:https://files.example.com/smiley.png
.
您应该看到您的测试文件,如预期的那样:
伟大的!现在,让我们看看响应中的那些 HTTP 标头。
从响应中删除 HTTP 标头
您可以使用 Chrome 开发人员工具查看响应标头,但我更喜欢curl 命令行工具。我使用该--head
参数来显示没有响应正文的 HTTP 标头,因为我的终端对二进制图像数据不满意!
注意:为了清晰和长度,我已经从这个和后续的 HTTP 响应中删除了一些无关的标头。
% curl --head https://files.superpat.com/smiley.png
HTTP/2 200
date: Thu, 20 Jan 2022 01:26:10 GMT
content-type: image/png
content-length: 23889
x-bz-file-name: smiley.png
x-bz-file-id: 4_zf1f51fb913357c4f74ed0c1b_f1163cc3f37a60613_d20220119_m204457_c004_v0402000_t0044
x-bz-content-sha1: 3cea1118fbaab607a7afd930480670970b278586
x-bz-upload-timestamp: 1642625097000
x-bz-info-src_last_modified_millis: 1642192830529
cache-control: max-age=14400
cf-cache-status: MISS
last-modified: Thu, 20 Jan 2022 01:26:10 GMT
我们的目标是删除所有x-bz
标题。创建一个 Modify Response Header 规则并将其名称设置为“Remove Backbaze B2 Headers”之类的名称。我们希望这条规则适用于所有流量,所以匹配表达式很简单:
不幸的是,没有办法告诉 Cloudflare 删除所有带有前缀的标头x-bz
,因此我们只需将它们全部列出:
保存规则,然后再次请求您的测试文件。您应该会看到更少的标题:
% curl --head https://files.superpat.com/smiley.png
HTTP/2 200
date: Thu, 20 Jan 2022 01:57:01 GMT
content-type: image/png
content-length: 23889
cache-control: max-age=14400
cf-cache-status: HIT
age: 1851
last-modified: Thu, 20 Jan 2022 01:26:10 GMT
通过 ETag 和 Cache-Control HTTP 标头优化缓存效率
我们可以效仿 James 的领导,通过利用标头提高缓存效率ETag
。如ETag 的 MDN Web 文档中所述:
(
ETag
或实体标签)HTTP 响应标头是资源特定版本的标识符。它使缓存更有效并节省带宽,因为如果内容没有更改,Web 服务器不需要重新发送完整的响应。
本质上,缓存可以只请求资源的 HTTP 标头,并且只有在资源主体发生更改时才继续获取资源主体ETag
。
James 按顺序使用 、 或 中的一个来x-bz-content-sha1
构造x-bz-info-src_last_modified_millis
ETag x-bz-file-id
。如果没有设置这些标头,那么也没有设置ETag
. James 想确保,如果x-bz-content-sha1
Backblaze B2 响应中不存在,他可以为 ETag 设置一个值。实际上,x-bz-content-sha1
在每个 Backblaze B2 响应中都作为 HTTP 标头存在,因此直接将其用作ETag
. 不过,尝试在转换规则中实现 James 的 Cloudflare Worker 逻辑是一个有趣的挑战!
不可能在转换规则中表达这种复杂程度,但我们可以对这个问题进行一点横向思考。我们可以轻松地连接三个标题以创建一个结果,该结果将在其中任何一个或多个更改时发生更改:
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])
请注意,给定的 HTTP 标头可能有多个值,http.response.headers["<header-name>"]
数组也是如此。http.response.headers["<header-name>"][0]
产生数组的第一个元素,在大多数情况下,也是唯一的元素。
编辑您刚刚创建的转换规则,将其名称更新为“Remove Backblaze B2 Headers, set ETag”,并添加一个具有动态值的标题:
不用担心订购;Cloudflare 将对操作重新排序,以便在“删除”之前发生“设置”。此外,如果响应中不存在这些标头,导致ETag
标头为空值,Cloudflare 将根本不会设置该标头。正是我们需要的行为!
另一个测试显示了结果。请注意,HTTP 标头不区分大小写,因此etag
与以下含义相同ETag
:
% curl --head https://files.superpat.com/smiley.png
HTTP/2 200
date: Thu, 20 Jan 2022 02:01:19 GMT
content-type: image/png
content-length: 23889
cache-control: max-age=14400
cf-cache-status: HIT
age: 2198
last-modified: Thu, 20 Jan 2022 01:24:41 GMT
etag: 3cea1118fbaab607a7afd930480670970b27858616421928305294_zf1f51fb913357c4f74ed0c1b_f1163cc3f37a60613_d20220119_m204457_c004_v0402000_t0044
另一个与缓存相关的标头是Cache-Control
,它告诉浏览器如何缓存资源。正如您在上述响应中看到的那样,Cloudflare 设置Cache-Control
为 a max-age
of14400 seconds
或 4 小时。
另一方面,James 的代码Cache-Control
根据对 B2 Cloud Storage 的请求是否成功进行设置。对于 200 的 HTTP 状态码,Cache-Control
设置为public
, max-age=31536000
,指示浏览器缓存响应 31,536,000 秒;换句话说,一年。对于任何其他 HTTP 状态,Cache-Control
设置为public
, max-age=300
,因此浏览器仅缓存响应五分钟。在这两种情况下,该public
指令都表明响应可以缓存在共享缓存中,即使请求包含Authorization
标头字段也是如此。
注意:我们实际上假设一旦创建,图像主机上的文件是不可变的。对于这个用例来说这通常是正确的,但是当您构建自己的解决方案时,您应该仔细考虑缓存策略。
目前,Cloudflare 转换规则不提供对 HTTP 状态代码的访问权限,但同样,我们只需稍加思考和调查即可满足要求。如上所述,对于成功的操作,Cloudflare 设置Cache-Control
为max-age=14400
或 4 小时。对于失败的操作,例如,请求不存在的对象,Cloudflare 会Cache-Control
从 Backblaze B2 的max-age=0
, no-cache
,传回标头no-store
。有了这些信息,就可以很简单地为成功案例构建一个max-age
从14400
到增加的转换规则:31536000
同样,我们需要使用[0]
来选择第一个匹配的 HTTP 标头。请注意,此规则对标头使用静态值 - 每个匹配的响应都相同。
我们将保留 B2 Cloud Storage 为失败情况设置的标头,尽管覆盖它同样容易。
另一项测试显示了我们努力的结果:
% curl --head https://files.superpat.com/smiley.png
HTTP/2 200
date: Thu, 20 Jan 2022 02:31:38 GMT
content-type: image/png
content-length: 23889
cache-control: public, max-age=31536000
cf-cache-status: HIT
age: 4017
last-modified: Thu, 20 Jan 2022 01:24:41 GMT
etag: 3cea1118fbaab607a7afd930480670970b27858616421928305294_zf1f51fb913357c4f74ed0c1b_f1163cc3f37a60613_d20220119_m204457_c004_v0402000_t0044
检查失败案例。正如预期的那样,Backblaze B2 的默认cache-control
标头是通过的。另外,请注意没有ETag
标头,因为 B2 Cloud Storage 没有返回任何x-bz
标头:
% curl --head https://files.superpat.com/badname.png
HTTP/2 404
date: Thu, 20 Jan 2022 02:32:35 GMT
content-type: application/json;charset=utf-8
content-length: 94
cache-control: max-age=0, no-cache, no-store
cf-cache-status: BYPASS
成功!浏览器和缓存将积极缓存响应,从而减轻 Cloudflare 和 Backblaze B2 的负担。
为图像文件设置 CORS 标头
我们快完成了!我们的最终要求是为图像设置跨域资源共享 (CORS)标头,以便可以在 Web 上任何域的网页中对它们进行操作。
转换规则必须匹配一系列文件扩展名,并设置Access-Control-Allow-Origin
HTTP 响应标头以允许任何网页访问资源:
上传一个文本文件并运行最后几个测试以查看结果。一、图片:
% curl --head https://files.superpat.com/smiley.png
HTTP/2 200
date: Thu, 20 Jan 2022 02:50:52 GMT
content-type: image/png
content-length: 23889
cache-control: public, max-age=31536000
cf-cache-status: HIT
age: 4459
last-modified: Thu, 20 Jan 2022 01:36:33 GMT
etag: 3cea1118fbaab607a7afd930480670970b27858616421928305294_zf1f51fb913357c4f74ed0c1b_f1163cc3f37a60613_d20220119_m204457_c004_v0402000_t0044
access-control-allow-origin: *
正如预期的Access-Control-Allow-Origin
那样,标题存在。
最后是文本文件,没有Access-Control-Allow-Origin
标题。您可以使用--include
参数而不是--head
查看文件内容和标题:
% curl --include https://files.superpat.com/hello.txt
HTTP/2 200
date: Thu, 20 Jan 2022 02:48:51 GMT
content-type: text/plain
content-length: 14
accept-ranges: bytes
cf-cache-status: DYNAMIC
etag: 60fde9c2310b0d4cad4dab8d126b04387efba28916426467400754_zf1f51fb913357c4f74ed0c1b_f1092902424a40504_d20220120_m024635_c004_v0402003_t0000
你好世界!
故障排除
在进行所有这些工作时,我遇到的最常见的问题是在引用 HTTP 标头时混淆了请求和响应。如果事情没有按预期工作,请仔细检查您没有需要的http.response.headers["<header-name>"]
地方,http.request.headers["<header-name>"]
反之亦然。
我真的可以免费吗?
Backblaze B2 定价非常简单:
贮存
前 10GB 的存储空间是免费的。
在 10GB 以上,我们收取 0.005 美元/GB/月的费用,大约是其他领先云对象存储(咳嗽、S3、咳嗽)成本的四分之一。
存储成本按小时计算,没有最低保留要求,按月计费。
下载数据
每天下载的前 1GB 数据是免费的。
超过 1GB,我们收取 0.01 美元/GB,但是……
通过我们的CDN 和计算合作伙伴(Cloudflare 就是其中之一)下载是免费的。
交易
每个下载操作都算作一个 B 类事务。
每天前 2,500 笔 B 类交易是免费的。
超过 2,500 笔 B 类交易,按每 10,000 笔 0.004 美元的费率收费。
没有惊喜账单
如果您已经注册了 Backblaze B2,您可能已经注意到您不必提供信用卡号。您的 10GB 免费存储空间永不过期,您也不会意外产生任何费用。
通过 Cloudflare 的全球 CDN 提供您的图像并如上所述优化您的缓存配置,您将不会从 B2 Cloud Storage 产生下载成本,并且可能保持在每天 2,500 次免费下载操作的范围内。同样,Cloudflare 的免费计划不需要信用卡激活,也没有数据或交易限制。
立即注册 Backblaze B2,部署您自己的个人图像主机,探索我们现成的集成,并考虑使用经济实惠、与 S3 兼容的云对象存储平台可以创建什么。
本文为转载文章,版权归原作者所有,不代表本站立场和观点。