- 数据存储
- 后端
【免费下载链接】opendal
Apache OpenDAL: One Layer, All Storage.
本文围绕 Apache OpenDAL 仓库中的边缘测试工程 s3_read_on_wasm,系统讲解如何让 OpenDAL 在wasm32-unknown-unknown目标(浏览器环境)上完成 S3 服务的写入与读取验证。读者将掌握从本地 S3 服务搭建、S3 配置参数解析、wasm-pack 构建到 Chrome 浏览器内运行wasm-bindgen-test的完整链路,并理解 OpenDAL 在 WASM 目标下 HTTP 传输层的底层适配原理。
背景:什么是 OpenDAL 的 Edge 测试
OpenDAL 的core工作区下维护着一个专门的边缘测试目录 core/edge。根据 core/edge/README.md 的说明,这些 edge 测试用于验证 OpenDAL 在各类"边缘"运行环境下的真实可用性,它们会预先准备数据并测试 OpenDAL 的功能是否按预期工作。
s3_read_on_wasm正是这些 edge 测试之一,其核心目标(文档原话)是:
This test is used to ensure opendal can run on WASM target.
也就是说,这个工程的存在是为了回答一个问题:把 OpenDAL 编译到 WASM 目标、跑在浏览器里,S3 服务的读写是否仍然可用?在浏览器环境中没有线程、没有异步运行时(如 tokio)、也没有原生 socket,因此这个测试直接关系到 OpenDAL 能否支撑 Web 端直接访问对象存储的场景。
工程结构与关键文件
s3_read_on_wasm工程位于仓库的 core/edge/s3_read_on_wasm 目录下,由三个文件构成:
| 文件 | 作用 |
|---|---|
| Cargo.toml | 声明 crate 类型、opendal 特性组合与 WASM 相关依赖 |
| src/lib.rs | 核心测试代码:构造 S3 Operator、写入并读回数据 |
| webdriver.json | 浏览器(Chrome/Firefox)驱动的附加配置 |
从 Cargo.toml 可以看到三个关键设计:
- crate 类型必须是
cdylib:[lib] crate-type = ["cdylib"],这是 wasm-bindgen 生成 JS 绑定所要求的产物形态; - opendal 采用精简特性组合:
opendal = { path = "../..", default-features = false, features = ["http-transport-reqwest", "services-s3"] },即关闭默认特性,只保留 reqwest HTTP 传输与 S3 服务两个能力,避免把与浏览器无关的服务(如 tokio 执行器、各类本地存储)带进 WASM 产物; - 配套 WASM 生态依赖:
wasm-bindgen负责 Rust/JS 互操作,wasm-bindgen-futures负责把 Rust future 接入浏览器事件循环,wasm-bindgen-test(dev-dependencies)负责在浏览器里运行测试。
其中opendal依赖指向path = "../..",即core工作区根目录(core/Cargo.toml),当前工作区版本为 0.59.4,MSRV 为 Rust 1.91。
第一步:准备本地 S3 服务
按照 README 的 Setup 章节,测试前需要在http://127.0.0.1:9900启动一个 S3 服务。实际仓库中常见的选择是 MinIO——它是与 AWS S3 API 完全兼容的对象存储,也正好匹配文档给出的minioadmin/minioadmin默认凭据。
启动后需要手动创建一个名为opendal(或按你的配置修改)的 bucket,并注意给服务开启允许浏览器跨域访问的 CORS 配置。这一点在浏览器环境是硬性前提:WASM 发出的 HTTP 请求遵循浏览器同源策略,目标服务必须返回正确的 CORS 响应头,否则请求会在进入业务逻辑前就被浏览器拦截。
第二步:S3 配置参数解析
README 给出的配置代码如下:
let mut cfg = S3::default(); cfg.endpoint("http://127.0.0.1:9900"); cfg.access_key_id("minioadmin"); cfg.secret_access_key("minioadmin"); cfg.bucket("opendal"); cfg.region("us-east-1");注意这里的S3::default()返回的是S3Builder(对应源码 core/services/s3/src/backend.rs 中的S3Builder),因此代码风格是链式let mut cfg = S3::default();后逐项调用 setter。各参数的语义与约束如下:
| 参数 | 说明 | 源码依据 |
|---|---|---|
endpoint | 服务完整地址,必须包含 scheme;代码会自动去掉末尾/。对 MinIO 这类 S3 兼容服务就是http://127.0.0.1:9900 | backend.rs#L116-L135 |
access_key_id | 访问密钥 ID,显式设置优先于环境变量读取 | backend.rs#L167-L177 |
secret_access_key | 访问密钥 Secret,显式设置优先于环境变量读取 | backend.rs#L179-L189 |
bucket | bucket 名称,必填项 | backend.rs#L109-L114 |
region | 签名区域。对自定义 endpoint 的服务(如 MinIO)该值实际不参与区域推导,按文档建议可保持us-east-1 | backend.rs#L137-L149 |
文档特别注明 "please feel free to change based on your own needs",因此端口、凭据、bucket 名都可以按实际环境调整。需要留意的是:README 中给出的端口是 9900、bucket 是opendal,而测试代码 src/lib.rs 中hello_world()实际使用的是http://127.0.0.1:9000和 buckettest——两者存在差异,实操时以你启动的 S3 服务为准,将配置与代码保持一致。
第三步:安装构建工具
构建与测试依赖wasm-pack,它是 Rust→WASM 打包与发布的主流工具链组件,安装方式:
cargo install wasm-pack安装完成后,工程中的wasm-pack build会把 Rust crate 编译为cdylibWASM 产物并生成配套的 JS/TS 胶水代码。
第四步:构建 WASM 产物
在core/edge/s3_read_on_wasm目录下执行:
wasm-pack build该命令默认针对wasm32-unknown-unknown目标构建,并输出pkg/目录,内含.wasm二进制与wasm-bindgen生成的 JS 绑定。
第五步:在 Chrome 中运行测试
执行测试:
wasm-pack test --chromeREADME 为这一步列出了三条重要注意事项,它们也反映了当前 WASM 测试能力的边界:
- 必须有 Chrome 已安装:测试会通过 WebDriver 拉起真实 Chrome 浏览器执行;
- 暂不能在 node.js 上运行:
wasm-bindgen-test的 Node 后端目前不适用,因此无法用--node方式跑该测试; - 暂不能在 headless Chrome 上运行:无头模式下测试不可靠,必须使用有头浏览器。
这三点约束同时体现在测试代码中——src/lib.rs 的测试模块使用了:
wasm_bindgen_test_configure!(run_in_browser);run_in_browser配置明确要求测试运行在真实浏览器上下文中(而非 Node),与 README 的说明相互印证。
另外,仓库还提供了 webdriver.json 作为浏览器驱动配置:它对 Chrome 传入--use-fake-device-for-media-stream等参数,对 Firefox 配置假媒体流偏好——这些是为了保证测试环境的一致性(例如避免浏览器权限弹窗阻塞测试)。
测试内容:写入并读回一个对象
测试的核心逻辑在 src/lib.rs 的hello_world函数中,它完整演示了"在 WASM 环境使用 OpenDAL 读写 S3"的最小闭环:
#[wasm_bindgen] pub async fn hello_world() -> String { opendal::install_default(); let cfg = S3::default() .endpoint("http://127.0.0.1:9000") .access_key_id("minioadmin") .secret_access_key("minioadmin") .bucket("test") .region("us-east-1"); let op = Operator::new(cfg).unwrap(); op.write( "test", "Hello, WASM! We are from OpenDAL at rust side!" .as_bytes() .to_vec(), ) .await .unwrap(); let bs = op.read("test").await.unwrap().to_bytes(); String::from_utf8_lossy(&bs).to_string() }几个值得注意的细节:
opendal::install_default()必须先调用:在 WASM 目标下没有ctor的main前自动注册(auto-register-services特性未启用),因此需要在函数入口手动安装默认 HTTP 传输。这正是 core/core/src/types/http_transport/mod.rs 中HttpTransporter::install_default机制的显式用法;async fn+wasm-bindgen:#[wasm_bindgen]支持将async fn导出为返回 Promise 的 JS 函数,配合wasm-bindgen-futures在浏览器事件循环中驱动 future;- 读写对称验证:先
op.write("test", ...)写入,再op.read("test")读回并断言内容一致,覆盖了 S3 服务在 WASM 下的双向数据通路。
对应的浏览器内测试用例:
#[wasm_bindgen_test] async fn test_hello_world() { let s = hello_world().await; assert_eq!(s, "Hello, WASM! We are from OpenDAL at rust side!") }测试通过#[wasm_bindgen_test]宏注册,由wasm-pack test --chrome在真实 Chrome 中执行;若断言失败(例如写入或读取出错),测试即失败,从而验证 OpenDAL 在 WASM 目标上的端到端可用性。
底层原理:WASM 目标下的 HTTP 传输适配
这个 edge 测试之所以能成立,依赖 OpenDAL 对 WASM 目标的系统级适配,主要体现在两个层面。
第一,HttpTransport特质按目标架构分叉。在 core/core/src/types/http_transport/mod.rs 中,HttpTransport::fetch有两条签名:
#[cfg(not(target_arch = "wasm32"))] fn fetch(&self, req: Request<Buffer>) -> impl Future<Output = Result<Response<HttpBody>>> + Send; #[cfg(target_arch = "wasm32")] fn fetch(&self, req: Request<Buffer>) -> impl Future<Output = Result<Response<HttpBody>>>;非 WASM 目标要求返回的 future 满足Send(可跨线程调度),而 WASM 目标不要求Send——因为浏览器是单线程事件循环,future 无需在线程间转移。这一设计从类型层面保证了 WASM 目标的编译与运行可行性。
第二,reqwest 在 WASM 目标上切换到浏览器 Fetch API。根据 core/http-transports/reqwest/README.md 的 "WASM targets" 章节:在wasm32-unknown-unknown和wasm32-none上,reqwest 不再使用 hyper 与原生 TLS,而是改用浏览器(或宿主环境)的 Fetch API 发请求,TLS 由浏览器环境提供。对应的实现细节也可以在 core/http-transports/reqwest/src/lib.rs 中看到:cfg(target_arch = "wasm32")分支下请求体通过reqwest::Body::from(body.to_bytes())整体装入、不设置 HTTP 版本字段等。因此,浏览器端 TLS(HTTPS)、CORS 等能力全部由宿主浏览器托管。
这也就解释了为什么这个 edge 测试必须"跑在真实 Chrome 里":WASM 代码的网络行为与浏览器 Fetch API、CORS 策略、TLS 栈深度绑定,只有真实浏览器才能完整验证这条链路。
常见问题排查
结合 README 的注意事项与上述源码分析,实操中如果测试失败,优先按以下顺序排查:
- S3 服务不可达:确认服务确实监听在配置的端口(README 写 9900,代码默认 9000),并确认 bucket 已创建;
- CORS 未配置:浏览器拦截请求时控制台会出现 CORS 相关报错,需要在 S3 服务侧允许来自测试页面的跨域访问;
- 未使用真实 Chrome:测试必须在已安装的有头 Chrome 上运行,
--node与 headless 模式均不受支持; - 凭据不一致:
access_key_id/secret_access_key必须与本地 S3 服务创建用户时设置的凭据一致(MinIO 默认minioadmin/minioadmin)。
小结
s3_read_on_wasm是 OpenDAL 针对 WASM 目标的代表性边缘测试:它以最小化的特性组合(http-transport-reqwest+services-s3)、cdylibcrate 形态和wasm-pack工具链,验证了 OpenDAL 在浏览器中直接读写 S3 的完整能力。其背后的HttpTransport按目标架构分叉签名、reqwest 切换到浏览器 Fetch API 等设计,共同构成了 OpenDAL WASM 支持的技术底座。如果你打算在浏览器端基于 OpenDAL 直连对象存储,这个工程就是最直接的参考样板——同样的模式也可以扩展到 opfs_wasm32(浏览器 OPFS 本地存储)等其他 WASM 场景。
- 数据存储
- 后端
【免费下载链接】opendal
Apache OpenDAL: One Layer, All Storage.
相关推荐
Apache OpenDAL Edge Tests 实战解析:磁盘写满、浏览器 WASM 与 AWS AssumeRole 边界场景验证
Apache OpenDAL Edge Tests 实战解析:磁盘写满、浏览器 WASM 与 AWS AssumeRole 边界场景验证 Apache Open
数据存储后端BoxMOT ReID 模型训练完全指南:从 `train-reid` 入门到 CSL-TinyViT 特权监督进阶
BoxMOT ReID 模型训练完全指南:从 train reid 入门到 CSL TinyViT 特权监督进阶 本指南以 BoxMOT 的 train rei
数据存储后端OpenDAL 磁盘写满场景测试:Fs 服务写入错误处理与 edge test 全解析
OpenDAL 磁盘写满场景测试:Fs 服务写入错误处理与 edge test 全解析 导读 本文围绕 Apache OpenDAL 仓库中的边缘测试(edge
数据存储后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考