☰
Apache OpenDAL 接入 Backblaze B2 云存储:配置、分片上传与预签名实战指南
2026/10/13 12:35:52 网站建设 项目流程
  • 数据存储
  • 后端

【免费下载链接】opendal

Apache OpenDAL: One Layer, All Storage.

项目地址:https://gitcode.com/gh_mirrors/op/opendal
点击查看免费下载

opendal-service-b2是 Apache OpenDAL 针对 Backblaze B2 对象存储的服务实现,它让基于 OpenDAL 构建的 Rust 应用(以及经由绑定层接入的 Java、Python、Node.js、Go 等语言)可以用一套统一 API 读写 B2 上的对象。本文以 core/services/b2/README.md 为骨架,结合 B2 服务源码 与配置定义,完整讲解如何启用该服务、配置必需参数、理解能力矩阵,并深入 B2 原生 API 调用链、分片上传与预签名 URL 的底层实现,帮助你快速、正确地落地 B2 存储接入。

一、服务定位:B2 在 OpenDAL 中的角色

Backblaze B2 是面向开发者的对象存储服务。opendal-service-b2(Cargo 包名opendal-service-b2)将其封装为 OpenDAL 中的一个 service,使得应用只需要面向Operator编程,就能读写 B2 bucket 中的文件。从源码结构看,服务模块包含清晰的职责划分:

  • backend.rs:B2Builder与B2Backend,负责配置校验、能力声明与各操作的入口分发;
  • config.rs:可序列化的B2Config,支持从 URI / 迭代器构造;
  • core.rs:B2Core,封装全部 B2 原生 API 调用(认证、上传、下载、分片、列表、复制、隐藏文件);
  • reader.rs、writer.rs、lister.rs、deleter.rs:分别实现流式读取、多段写入、分页列表与批量删除。

服务注册的 URI scheme 为b2,定义在 lib.rs。

二、快速接入:两种依赖引入方式

1. 通过opendal门面启用(推荐)

大多数应用只需要引入统一的opendalcrate,并开启services-b2feature 即可:

cargo add opendal --features services-b2

该 feature 在 core/Cargo.toml 中声明为services-b2 = ["dep:opendal-service-b2"],对应可选依赖 opendal-service-b2。启用后,服务以opendal::services::B2暴露,配置好 builder 后传给opendal::Operator::new:

use opendal::Operator; use opendal::Result; use opendal::services::B2; #[tokio::main] async fn main() -> Result<()> { let mut builder = B2::default() .root("/") .application_key_id("xxxxxxxxxx") .application_key("xxxxxxxxxx") .bucket("opendal") .bucket_id("xxxxxxxxxxxxx"); let op: Operator = Operator::new(builder)?; Ok(()) }

2. 直接使用拆分后的 crate

如果项目需要精细控制依赖体积或直接使用opendal-core,可以分别引入拆分 crate:

cargo add opendal-core opendal-service-b2

对应的构造方式为(与 README.md 一致):

use opendal_core::{Operator, OperatorRegistry, Result}; use opendal_service_b2::{register_b2_service, B2}; fn build_operator(builder: B2) -> Result<Operator> { Operator::new(builder) } fn register_for_uri() { register_b2_service(OperatorRegistry::get()); }

其中register_for_uri仅在使用 scheme 驱动的构造方式(Operator::from_uri或Operator::via_iter)时才需要调用;通过Operator::new直接构造不需要注册。此外,B2 服务会发送 HTTP 请求,因此需要在OperationContext中提供 HTTP transport(详见opendal-core的组合示例)。

三、配置项详解与校验规则

B2Config共五个字段,定义于 config.rs:

配置项类型是否必填说明
rootOption<String>否后端根路径,所有操作都将发生在此 root 之下
application_key_idOption<String>条件必填keyID;显式设置时优先取用户输入,否则尝试从环境变量加载
application_keyOption<String>条件必填applicationKey;显式设置时优先取用户输入,否则尝试从环境变量加载
bucketString必填桶名称,可在 Backblaze B2 控制台的 buckets 页面查看
bucket_idString必填桶 ID,同样在控制台页面查看

从 backend.rs 的Builder::build实现可以看到校验逻辑:

  • bucket为空 → 返回ConfigInvalid错误,错误信息"bucket is empty";
  • bucket_id为空 → 返回ConfigInvalid错误,错误信息"bucket_id is empty";
  • application_key_id与application_key均要求非空,否则同样以ConfigInvalid拒绝构建。

builder 方法对空字符串的处理:.root("")、.application_key_id("")、.application_key("")会把对应字段重置为None(backend.rs),让后续逻辑退回环境变量加载路径。

通过 URI 与迭代器构造

B2Config实现了Configurator,支持 scheme 驱动构造。在 config.rs 的from_uri中:URI 的名称(uri.name())自动映射为bucket,URI 路径(uri.root())映射为root,其余参数从 query options 中解析。仓库内 config.rs 的单元测试验证了b2://example-bucket/path/to/root?bucket_id=bucket-id这种 URI 形式能正确提取bucket、root与bucket_id。

四、能力矩阵:B2 服务支持哪些操作

src/docs.md 给出了官方的能力清单,与 backend.rs 中Capability的声明完全对应:

能力支持情况说明 / 源码依据
create_dir❌在 backend.rs 中直接返回Unsupported
stat✅通过list_file_names获取文件信息(B2 无独立的 get_file_info 按名查询接口)
read✅流式读取,支持read_with_suffix(Range 请求)
write✅支持空文件、多段写入、Content-Type、用户元数据
delete✅基于 B2 的b2_hide_file(隐藏文件)实现
list✅支持list_with_limit、list_with_start_after、list_with_recursive
copy✅调用b2_copy_file
rename❌在 backend.rs 中返回Unsupported
presign✅支持presign_read、presign_write、presign_stat

值得注意的分片写入边界(backend.rs):

  • 最小分片大小(write_multi_min_size)为5 MiB(5 × 1024 × 1024),这是 B2 对 large file 分片的下限;
  • 最大分片大小(write_multi_max_size)在 64 位平台上为5 GiB,对应 B2 的上限;
  • 单文件最大分片数为 10000(writer.rs 中注释说明 part number 必须落在[1..=10000])。

构建完 Operator 后,可通过opendal_core::Operator::info()与OperatorInfo::capability()检查实际生效的能力集。

五、底层原理:B2 原生 API 调用链

1. 账户认证与 Token 缓存

所有 API 调用前都需要先认证。B2Core::get_auth_info(core.rs)调用b2_authorize_account:

  • 使用 application key ID 与 key 构造 Basic Auth 请求头,请求https://api.backblazeb2.com/b2api/v2/b2_authorize_account;
  • 响应包含authorizationToken、apiUrl(普通 API 调用的基础 URL)与downloadUrl(下载文件的基础 URL);
  • 该 token 官方最长有效 24 小时,OpenDAL 实现将其有效期设置为 20 小时(expires_in: Timestamp::now() + Duration::from_secs(20 * 60 * 60)),并在B2Signer(受RwLock保护)中缓存;
  • 后续调用先读缓存,未过期直接复用,过期才重新认证,避免频繁授权。

2. 读取:按名下载与 Range 请求

B2Reader::open(reader.rs)走download_file_by_name:拼接{downloadUrl}/file/{bucket}/{path}后携带授权 token 发起 GET;当请求带 Range 且非全量时附加Range头,B2 返回200 OK或206 Partial Content均视为成功,并把响应体包装为流式读取器。

3. 写入:单文件上传与多段(large file)上传

write入口(backend.rs)统一包装为oio::MultipartWriter,由 writer.rs 中的B2Writer实现MultipartWritetrait:

  • write_once(小文件):b2_get_upload_url获取上传 URL 后,携带X-Bz-File-Name、X-Bz-Content-Sha1(写死do_not_verify)、Content-Length、Content-Type(默认b2/x-auto)与Content-Disposition等头直接b2_upload_file;
  • 分片上传(大文件):依次调用b2_start_large_file(返回file_id)→b2_get_upload_part_url+b2_upload_part(带X-Bz-Part-Number,part number 从 1 起)→b2_finish_large_file(提交各分片 SHA1 数组);
  • 中止:abort_part调用b2_cancel_large_file取消未完成的大文件上传。

用户元数据在两种路径下的传递方式不同(core.rs):单文件上传使用X-Bz-Info-前缀头,分片上传则放入fileInfo字段;读取元数据时(parse_file_info,core.rs)会去掉前缀并对值做 URL 解码。

4. 列表与 stat:基于b2_list_file_names

由于 B2 没有"按文件名取文件信息"的独立 API,stat与list都复用了b2_list_file_names(core.rs):

  • 查询参数支持prefix、maxFileCount(对应list_with_limit)、startFileName(对应list_with_start_after)与delimiter(递归列表传None,非递归传/,见 lister.rs);
  • stat在路径以/结尾时传入 delimiter 以命中目录对象;路径为/时直接返回目录元数据(backend.rs);
  • 分页依赖响应中的nextFileName,作为下一页的startFileNametoken(lister.rs)。

5. 删除:隐藏文件语义

B2 的"删除"本质是隐藏文件。B2Deleter::delete_once(deleter.rs)调用b2_hide_file;若返回NotFound或AlreadyExists(对应 B2 的already_hidden错误码)都视为删除成功,代表幂等语义。

6. 复制

copy(backend.rs)先通过list_file_names获取源文件的file_id(目录对象无 file_id,返回IsADirectory),再调用b2_copy_file以source_file_id + 目标文件名完成服务端复制。

六、预签名 URL:生成短期授权链接

B2 服务实现了presign,支持三种操作(backend.rs):

  • presign_stat / presign_read:调用b2_get_download_authorization,以bucketId、fileNamePrefix和validDurationInSeconds(即expire秒数)换取授权 token,拼接{downloadUrl}/file/{bucket}/{percent-encoded-path}?Authorization={token};Read 且 Range 非全量时附带Range头;
  • presign_write:b2_get_upload_url换取上传 URL 与 token,构造 POST 请求,附上X-Bz-File-Name、Content-Type: b2/x-auto与X-Bz-Content-Sha1: do_not_verify头;
  • presign_delete:不支持,返回Unsupported。

生成的PresignedRequest可直接交给客户端执行,适合浏览器直传、CDN 回源等场景。

七、错误处理与可重试语义

B2 的错误响应会被统一解析为 OpenDAL 的ErrorKind(core.rs):

  • HTTP 403 →PermissionDenied;404 →NotFound;304/412 →ConditionNotMatch;401 →PermissionDenied(可重试);429 →RateLimited(可重试);500/502/503/504 →Unexpected(可重试);
  • B2 业务错误码进一步细化:already_hidden→AlreadyExists,no_such_file→NotFound(core.rs)。

这些映射有对应的单元测试(core.rs)覆盖,可重试的错误会被标记set_temporary,供上层重试层决策。

八、多语言接入参考

B2 服务同样暴露给各语言绑定,website/data/services.json 中收录了各绑定的最小化构造示例,例如:

import opendal operator = opendal.Operator( "b2", bucket="...", bucket_id="...", )
import { Operator } from "opendal"; const operator = new Operator("b2", { bucket: "...", bucket_id: "...", });

Go 绑定通过opendal.NewOperator(b2.Scheme, opendal.OperatorOptions{...})构造;Java 绑定通过Operator.of("b2", configMap)构造。各绑定均可传入可选的root、application_key_id、application_key,必填项始终是bucket与bucket_id。

九、总结

opendal-service-b2用约 6 个 Rust 模块完整封装了 Backblaze B2 的对象存储能力:cargo add opendal --features services-b2即可接入,bucket与bucket_id为必填配置;服务原生支持 stat、read、write(含 5 MiB~5 GiB 分片)、delete、list、copy 与 presign 三类操作,不支持 create_dir 与 rename。其实现细节——20 小时 token 缓存、基于b2_list_file_names的 stat、隐藏文件式删除、分片 SHA1 提交——都是可以直接查阅 core.rs、writer.rs 与 backend.rs 验证的事实,可作为接入 B2 时的最佳参考。

  • 数据存储
  • 后端

【免费下载链接】opendal

Apache OpenDAL: One Layer, All Storage.

项目地址:https://gitcode.com/gh_mirrors/op/opendal
点击查看免费下载

相关推荐

上一篇:网盘直链下载助手终极教程:轻松获取九大网盘真实下载地址的免费解决方案
下一篇:如何在Kodi中实现115网盘原码播放:3步配置完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询