为 Cloudreve 添加 Backblaze B2 支持

目前最新的配置教程已经同步到 Cloudreve 官方文档
如果你需要更简略的版本请直接查阅官方文档
官方文档那个也是我写的

问题分析

首先按照 Cloudreve 官方文档 进行配置，然后你大概率会发现配置完之后上传依然会报错 （废话，要是不报错也不会写这篇文章）

打开 F12 看一下请求，控制台这边有一个跨域报错

Copy
Access to XMLHttpRequest at 'https://s3.us-east-001.backblazeb2.com/xxx' from origin 'https://demo.cloudreve.org' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

然后检查下响应标头

Copy
Cache-Control: max-age=0, no-cache, no-store
Connection: keep-alive
Content-Length: 216
Content-Type: application/xml
Date: Wed, 19 Jun 2024 15:54:31 GMT
Server: nginx
Strict-Transport-Security: max-age=63072000
X-Amz-Id-2: aM+Y1ZDJYZBU2hn2JYjDlOMNkuWF2
X-Amz-Request-Id: 4c38if3n9n98732

很明显，缺了跨域头

这里我们再拿 Cloudflare R2 的响应标头对比下

Copy
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag
Cf-Ray: 89649ob992b2d0f-LAX
Connection: keep-alive
Content-Length: 0
Date: Wed, 19 Jun 2024 15:56:54 GMT
Etag: "264f5757014d2dn3ij3ij2fc90cd7b"
Server: cloudflare
Vary: Origin, Accept-Encoding

发现缺了什么没有，重点在于

Copy
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag

上面那个是跨域，下面那个是断点续传需要的

解决方案

首先先在 Backblaze 后台配置 CORS 规则

然后你会发现并没有什么用，由于 Backblaze 极为 简洁 (jian lou) 的后台页面，你没法直接在 Web 后台进行响应头的修改（这里改的是读取文件的请求头，上传的没法改）
所以我们得在 B2 CLI 进行修改

首先先去 Application Keys 新建一个登录密钥（这里用 Master Application Key 或者 Application Key 应该都行，Application Key 的话自己给下权限）
然后下载 B2 CLI

如果你是 Linux 用户，你可以运行以下的命令来下载 B2 CLI

Copy
# 下载最新二进制
wget https://github.com/Backblaze/B2_Command_Line_Tool/releases/latest/download/b2-linux
# 给予可执行权限
chmod +x ./b2-linux

执行 b2 account authorize 进行账号登录，根据提示填入 keyID 和 applicationKey
当你看到输出了类似下面的一大坨东西，就说明登录成功了

Copy
$ ./b2-linux account authorize
Backblaze application key ID: <keyID>
Backblaze application key:
{
"accountAuthToken": "<Token>",
"accountFilePath": "/home/bakaArch/.config/b2/account_info",
"accountId": "<accountID>",
"allowed": {
"bucketId": null,
"bucketName": null,
"capabilities": [
"bypassGovernance",
"deleteBuckets",
"deleteFiles",
"deleteKeys",
"listAllBucketNames",
"listBuckets",
"listFiles",
"listKeys",
"readBucketEncryption",
"readBucketNotifications",
"readBucketReplications",
"readBucketRetentions",
"readBuckets",
"readFileLegalHolds",
"readFileRetentions",
"readFiles",
"shareFiles",
"writeBucketEncryption",
"writeBucketNotifications",
"writeBucketReplications",
"writeBucketRetentions",
"writeBuckets",
"writeFileLegalHolds",
"writeFileRetentions",
"writeFiles",
"writeKeys"
],
"namePrefix": null
},
"apiUrl": "https://api001.backblazeb2.com",
"applicationKey": "<applicationKey>",
"applicationKeyId": "<applicationKeyId>",
"downloadUrl": "https://f001.backblazeb2.com",
"isMasterKey": true,
"s3endpoint": "https://s3.us-east-001.backblazeb2.com"
}

首先，执行 b2 bucket get <bucketName> 列出一下存储桶详情

Copy
{
"accountId": "<accountID>",
"bucketId": "<bucketID>",
"bucketInfo": {},
"bucketName": "<bucketName>",
"bucketType": "allPrivate",
"corsRules": [
{
"allowedHeaders": [
"authorization",
"range"
],
"allowedOperations": [
"b2_download_file_by_id",
"b2_download_file_by_name"
],
"allowedOrigins": [
"*"
],
"corsRuleName": "downloadFromAnyOrigin",
"exposeHeaders": null,
"maxAgeSeconds": 3600
},
{
"allowedHeaders": [
"authorization",
"range"
],
"allowedOperations": [
"s3_head",
"s3_get"
],
"allowedOrigins": [
"*"
],
"corsRuleName": "s3DownloadFromAnyOrigin",
"exposeHeaders": null,
"maxAgeSeconds": 3600
}
],
"defaultRetention": {
"mode": null
},
"defaultServerSideEncryption": {
"mode": "none"
},
"isFileLockEnabled": false,
"lifecycleRules": [],
"options": [
"s3"
],
"replication": {
"asReplicationDestination": null,
"asReplicationSource": null
},
"revision": 7
}

注意这里 corsRules，可以看到这里的 allowedOperations 只有 s3_head 和 s3_get 方法（如果你没有在 Web 端配置跨域，那这两条显然你也是见不到的）
而为了能够在 Cloudreve 中正常操作文件，我们还需要为其添加 s3_put s3_post s3_delete 方法
同时，为了能够在 S3 兼容 API 中正常进行分块上传，我们还需要允许 B2 返回 ETag
我们在前面复制的存储桶详情中，修改 corsRules 中的 allowedOperations，在 exposeHeaders 中添加 ETag，在 allowedHeaders 中添加 content-type
修改完之后应该是长这样

Copy
"corsRules": [
{
"allowedHeaders": [
"authorization",
"range",
"content-type"
],
"allowedOperations": [
"s3_head",
"s3_get",
"s3_put",
"s3_post",
"s3_delete"
],
"allowedOrigins": [
"*"
],
"corsRuleName": "s3DownloadFromAnyOriginWithUpload",
"exposeHeaders": ["ETag"],
"maxAgeSeconds": 3600
}
]

因为通过 Cloudreve 上传文件只需要用到 S3 兼容 API，所以我这边直接删去了 B2 的部分

如果你希望得到更好的安全性，可以将 allowedOrigins 中的星号换成你 Cloudreve 的域名

修改完之后，使用命令 b2 bucket update <bucketName> --cors-rules '配置json' 更新配置
更新完之后会重新输出一遍完整配置，检查一下是否正确

Copy
$ ./b2-linux bucket update <bucketName> --cors-rules '[
{
"allowedHeaders": [
"authorization",
"range",
"content-type"
],
"allowedOperations": [
"s3_head",
"s3_get",
"s3_put",
"s3_post",
"s3_delete"
],
"allowedOrigins": [
"*"
],
"corsRuleName": "s3DownloadFromAnyOriginWithUpload",
"exposeHeaders": ["ETag"],
"maxAgeSeconds": 3600
}
]'

{
"accountId": "<accountID>",
"bucketId": "<bucketID>",
"bucketInfo": {},
"bucketName": "<bucketName>",
"bucketType": "allPrivate",
"corsRules": [
{
"allowedHeaders": [
"authorization",
"range",
"content-type"
],
"allowedOperations": [
"s3_head",
"s3_put",
"s3_delete",
"s3_post",
"s3_get"
],
"allowedOrigins": [
"*"
],
"corsRuleName": "s3DownloadFromAnyOriginWithUpload",
"exposeHeaders": [
"etag"
],
"maxAgeSeconds": 3600
}
],
"defaultRetention": {
"mode": null
},
"defaultServerSideEncryption": {
"mode": "none"
},
"isFileLockEnabled": false,
"lifecycleRules": [],
"options": [
"s3"
],
"replication": {
"asReplicationDestination": null,
"asReplicationSource": null
},
"revision": 9
}

到这里，应该就没有问题了
再次尝试上传文件，问题解决

此文由 Mix Space 同步更新至 xLog
原始链接为 https://blog.baka.plus/posts/tech/Add-Backblaze-B2-support-to-Cloudreve