cloudflare-docs Miniflare R2 指南:用 r2Buckets 与 getR2Bucket() 在本地仿真测试 Workers 存储
2026/9/18 9:34:12 网站建设 项目流程

cloudflare-docs Miniflare R2 指南:用 r2Buckets 与 getR2Bucket() 在本地仿真测试 Workers 存储

【免费下载链接】cloudflare-docsCloudflare’s documentation项目地址: https://gitcode.com/GitHub_Trending/cl/cloudflare-docs

本篇基于 cloudflare-docs 仓库中的 Miniflare 存储文档 R2,完整讲解如何在 Miniflare 测试环境中声明本地 R2 Bucket、如何在 Worker 内部与 Worker 外部读写 R2 对象,以及如何将这套能力嵌入可运行的测试套件。读完后,你将能够独立完成:声明r2Buckets绑定、通过getR2Bucket()在测试代码中直接 put/get 对象、验证 Worker 与测试代码共享同一份本地存储数据,以及用r2Persist控制数据持久化。

为什么需要 Miniflare 的 R2 本地仿真

Miniflare API 的定位是让开发者在不发起真实 HTTP 请求的情况下向 Worker 派发事件、模拟 Worker 之间的连接,并与本地仿真的存储产品交互。根据 Miniflare 入门文档 的说明,其本地仿真覆盖了 KV、R2、Durable Objects 等存储产品(D1、Cache 亦在 完整选项参考 中列出)。

这意味着在测试 R2 相关逻辑(例如以对象存储实现计数器、缓存层)时,无需连接任何真实 R2 账户或网络,所有env.BUCKET.put()/env.BUCKET.get()调用都作用于一个进程内的本地存储实例,测试因此具备确定性、离线性与零成本三个特性。

需要特别注意的是一个前提约束:Miniflare 不会读取 Wrangler 的配置文件,Worker 用到的所有绑定都必须在 Miniflare API 的选项中显式指定(见 Writing tests 中的注意事项)。因此本地测试 R2 时,Bucket 的声明只能出现在 Miniflare 构造选项里,而不能依赖wrangler.jsonc中的r2_buckets配置。

声明 R2 Buckets:r2Buckets 选项

以字符串数组声明

为环境添加 R2 Bucket 最直接的方式是通过r2Buckets选项传入字符串数组:

const mf = new Miniflare({ r2Buckets: ["BUCKET1", "BUCKET2"], });

数组中的每个名字既是 Bucket 名称,同时也是该 Bucket 在 Worker 运行时环境中的绑定名——Worker 内部即可通过env.BUCKET1env.BUCKET2直接访问对应 Bucket,无需任何额外映射。

v3 支持的 Record 形式:绑定名与 Bucket 名分离

从 Miniflare v2 迁移文档 可以看到,v3 中kvNamespaces/r2Buckets/d1Databases这些选项除了接受string[]外,还接受Record<string, string>形式,即“绑定名 -> Bucket 名/命名空间 ID”的映射。这一变化的实际收益是:多个 Worker 可以以不同的绑定名绑定到同一个 Bucket。这在测试多 Worker 架构(例如一个 Worker 写入、另一个 Worker 读取)时尤为有用:

// 示意:两个 Worker 以不同绑定名访问同一个本地 Bucket const mf = new Miniflare({ workers: [ { name: "writer", r2Buckets: { MY_BUCKET: "shared-bucket" }, // ... }, { name: "reader", r2Buckets: { STORE: "shared-bucket" }, // ... }, ], });

无论采用哪种形式,Worker 内部的访问方式一致:拿到env.<绑定名>后,即可调用与 R2 Workers API 参考 中相同的get/put等接口。

在 Worker 外部操作 R2:getR2Bucket()

对测试而言,常常需要在 Worker 之外直接往 R2 存储里写入或读取数据——例如预置初始状态、断言 Worker 处理后的存储结果。Miniflare 提供了getR2Bucket方法来完成这件事(方法本身在 Miniflare 参考 中列出)。

下面完整继承自 R2 文档 的示例演示了这一点:一个 Worker 读取count对象、加 1 后写回;测试代码先预置count"1",再向 Worker 发请求,两边共享同一份本地存储:

import { Miniflare } from "miniflare"; const mf = new Miniflare({ modules: true, script: ` export default { async fetch(request, env, ctx) { const object = await env.BUCKET.get("count"); const value = parseInt(await object.text()) + 1; await env.BUCKET.put("count", value.toString()); return new Response(value.toString()); } } `, r2Buckets: ["BUCKET"], }); const bucket = await mf.getR2Bucket("BUCKET"); await bucket.put("count", "1"); const res = await mf.dispatchFetch("http://localhost:8787/"); console.log(await res.text()); // 2 console.log(await (await bucket.get("count")).text()); // 2

逐步拆解该示例的执行链:

  1. r2Buckets: ["BUCKET"]声明本地 Bucket,Worker 内对应env.BUCKET
  2. mf.getR2Bucket("BUCKET")在测试进程中拿到同一 Bucket 的句柄。从文档示例的结构看(与 KV 的 getKVNamespace 示例 完全平行),外部句柄与 Worker 内env.BUCKET指向同一存储,这正是测试断言的基础;
  3. bucket.put("count", "1")预置初始值——此时还没有任何请求到达 Worker;
  4. mf.dispatchFetch("http://localhost:8787/")向本地 HTTP 服务器派发 fetch 事件。Miniflare 默认监听 8787 端口(见 完整选项参考 中port: 8787的默认值注释),派发后 Worker 读到"1"、加 1 写回"2"并响应"2"
  5. 最后通过外部句柄再读一次count,确认持久化的结果同样是"2",完成“Worker 写、测试读”的闭环验证。

getR2Bucket()返回的句柄在 API 层面与 Worker 内的 R2 绑定一致:put(key, value)写入字符串值,get(key)返回对象后需再调用.text()(或.arrayBuffer()等)解出内容,这与 R2 Workers API 的对象读取语义相同。

此外,Miniflare 还提供了更通用的getBindings()方法,可一次性拿到环境中所有绑定(KV/R2 命名空间、变量等)的句柄。Writing tests 文档 展示的就是这种用法:

const bindings = await worker.getBindings(); // bindings.BUCKET 即与 env.BUCKET 相同的 R2 绑定

如果你倾向通过getBindings()而非getR2Bucket()访问存储,两种方式在 R2 场景下是等价的,可按测试框架的组织习惯选用。

R2 数据持久化:r2Persist 选项

与 Miniflare 中 Durable Objects(durableObjectsPersist,默认.mf/do,见 Durable Objects 文档)和 Cache(cachePersist,默认./.mf/cache,见 Cache 文档)的平行设计相类似,R2 的持久化由r2Persist选项控制。在 Miniflare 完整选项参考 中可以看到该选项的注释:

const mf = new Miniflare({ r2Buckets: ["BUCKET"], // R2 bucket to bind r2Persist: "./r2-data", // Persist R2 data (to optional path) });

r2Persist接受一个目录路径,用于将 R2 数据持久化到文件系统的指定位置。从与 Durable Objects、Cache 两个兄弟文档的对照看,不设置该选项时数据在内存中维护,可以在同一Miniflare实例的 reload 之间保留,但不跨实例;而需要跨进程或跨测试会话保留对象数据(例如调试期间反复重启测试进程仍希望看到之前写入的对象)时,才应启用r2Persist并指定路径。

嵌入测试套件:一个完整的可运行形态

将上述能力放入测试框架,结构上与 Writing tests 文档 的node:test范式一致:before中构造并等待ready,测试用例中派发请求或操作绑定,afterdispose()清理。一个针对 R2 计数逻辑的完整测试骨架如下(示例脚本内容与 R2 文档 完全一致):

import assert from "node:assert"; import test, { after, before } from "node:test"; import { Miniflare } from "miniflare"; test("r2 counter worker", async () => { const mf = new Miniflare({ modules: true, script: ` export default { async fetch(request, env, ctx) { const object = await env.BUCKET.get("count"); const value = parseInt(await object.text()) + 1; await env.BUCKET.put("count", value.toString()); return new Response(value.toString()); } } `, r2Buckets: ["BUCKET"], }); await mf.ready; try { // 测试侧预置初始状态 const bucket = await mf.getR2Bucket("BUCKET"); await bucket.put("count", "1"); // 通过本地 HTTP 派发 fetch 事件 const res = await mf.dispatchFetch("http://localhost:8787/"); assert.strictEqual(await res.text(), "2"); // 断言 Worker 写回后的存储状态 assert.strictEqual(await (await bucket.get("count")).text(), "2"); } finally { await mf.dispose(); } });

该骨架的关键点:

  • modules: truescript字符串用于在测试内联定义 Worker,无需落盘脚本文件;若 Worker 是实际项目文件,改用scriptPathmodules模块图方式(见 Writing tests 中“More complex Workers”);
  • await mf.ready等待本地 HTTP 服务器就绪后再发起请求,入门文档 明确建议等待ready属性;
  • dispose()用于断开存储数据库连接、停止监听,测试收尾时不可省略;
  • 测试代码运行在 Node.js 进程中,只有 Worker 本身运行在workerd运行时内(见 Writing tests 的运行时说明),因此“测试侧读写 R2”走的是 Miniflare 暴露的绑定句柄,而不是 Worker 内部代码路径。

与同类存储文档的关系及延伸阅读

Miniflare 的 R2 文档与同目录下的 KV、Durable Objects、D1、Cache 文档采用完全一致的叙述结构:“声明绑定 -> Worker 内访问 -> 通过外部方法操作(getKVNamespace/getDurableObjectNamespace/getD1Database/getCaches)”。R2 对应的三方即:r2Buckets声明、env.BUCKET内访问、getR2Bucket()外操作。掌握其中任一篇的套路,其余存储类型的测试写法可直接迁移。

其余参考入口:

  • Miniflare 入门与完整选项参考:r2Buckets/r2Persist的完整选项注释、dispatchFetchgetWorker事件派发、setOptions/dispose生命周期;
  • Writing tests:Miniflare 在node:test中的完整集成范式,以及“Miniflare 不读取 Wrangler 配置”的前提约束;
  • Miniflare v2 -> v3 迁移:r2Buckets等选项接受Record<string, string>的变更说明;
  • R2 Workers API 参考:Worker 内env.BUCKET绑定的完整 R2 接口(get/put/list等),本地仿真遵循相同的 API 语义。

小结

Miniflare 的 R2 仿真由三个要素构成:r2Buckets选项声明本地 Bucket 并自动形成env绑定;Worker 内部按标准 R2 Workers API 读写对象;测试代码通过getR2Bucket()(或getBindings())拿到与 Worker 共享的存储句柄,完成预置状态与结果断言。配合r2Persist控制跨会话持久化、await mf.readydispose()管理生命周期,即可在完全离线的环境中构建确定性的 R2 存储集成测试。

【免费下载链接】cloudflare-docsCloudflare’s documentation项目地址: https://gitcode.com/GitHub_Trending/cl/cloudflare-docs

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

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

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

立即咨询