三、数据访问
Blob 数据持久化在云端的对象存储中,并通过边缘节点加速读取。读取请求由离用户最近的边缘节点响应,毫秒级返回。
四、一致性模型
Blob 提供两种一致性模式,可按需选择:
| 模式 | 读取行为 | 适用场景 |
| 最终一致(默认) | 走边缘缓存,速度最快;新写入的数据需要短暂时间(通常秒级)才能在所有节点上读到。 | 内容展示、缓存读取、容忍短暂不一致的业务。 |
| 强一致 | 跳过缓存直读主存储,保证读到最新值;读取耗时略高。 | 计数器、状态机、必须立即读到最新写入值的业务。 |
调用 store.get(key) 时默认使用最终一致;传入 { consistency: "strong" } 即切换到强一致。也可在 getStore 时统一指定整个 Store 的默认一致性。
注意:强一致性会增加读取耗时,仅在确有必要时使用。
六、用户文件上传
接收用户上传的头像、附件、图片等文件,存储到 Blob 中按用户或业务维度组织。
七、AI 生成内容
存储 AI 模型生成的图片、文档、报告,按批次或类型目录管理,方便检索和展示。
八、结构化数据集
将多个 JSON 文件按目录组织存放,通过 list 遍历处理整个数据集。
十、1. 安装 SDK
npm install @edgeone/pages-blob
十一、2. 在 Makers Functions 中使用
import { getStore } from "@edgeone/pages-blob";export async function onRequest({ request }) { const store = getStore("my-store"); // 写入 await store.set("hello.txt", "Hello, EdgeOne Makers!"); const content = await store.get("hello.txt"); return new Response(content);}
首次调用 getStore("my-store") 时,平台会自动为当前项目创建名为 my-store 的命名空间,后续调用直接使用已有的命名空间。
十二、3. 部署
部署 Makers 项目后,触发一次请求即可在控制台的 Blob 存储页面中看到已创建的命名空间和对象。
十三、控制台使用
Blob 在控制台中仅支持只读浏览(查看命名空间列表、浏览对象目录结构)。命名空间的创建和所有数据操作均通过 SDK 完成,详见下方 API 章节。
十四、API
import { getStore, listStores } from "@edgeone/pages-blob";
十五、getStore(name | options)
获取一个 Store 实例。getStore 接受两种入参:
·直接传入命名空间名称字符串,如 getStore("my-store")
·传入配置对象,如 getStore({ name, projectId, token, consistency })
在 Makers Functions 中
只需指定命名空间名称:
const store = getStore("my-store");// 也可使用对象形式,便于同时指定默认一致性const store = getStore({ name: "my-store", consistency: "strong" });
在 Makers Functions 之外访问(例如本地脚本、外部服务)
需要额外提供项目 ID 和 API Token:
const store = getStore({ name: "my-store", projectId: "pages-urtsvuwmfvli", token: "your-api-token",});
参数
| 参数 | 类型 | 必填 | 说明 |
| name | string | 是 | 命名空间名称。 |
| projectId | string | 在 Makers Functions 之外访问时必填 | 目标项目 ID。 |
| token | string | 在 Makers Functions 之外访问时必填 | API Token。 |
| consistency | "eventual" | "strong" | 否 | 默认读取一致性,默认 "eventual"。 |
十六、store.set(key, value, options?)
写入一个对象。Key 已存在则覆盖。
await store.set("photos/cat.jpg", imageBuffer);await store.set("notes/todo.txt", "Buy milk");
参数
| 参数 | 类型 | 必填 | 说明 |
| key | string | 是 | 对象的 Key。 |
| value | string | ArrayBuffer | Blob | ReadableStream | 是 | 对象内容。 |
返回值
Promise<void>
十七、store.setJSON(key, value, options?)
写入 JSON 对象,自动序列化。接受与 store.set 相同的 options。
await store.setJSON("user/preferences", { theme: "dark", lang: "zh-CN" });
参数
| 参数 | 类型 | 必填 | 说明 |
| key | string | 是 | 对象的 Key。 |
| value | any | 是 | 可序列化为 JSON 的数据。 |
返回值
Promise<void>
十八、store.get(key, options?)
读取一个对象。Key 不存在时返回 null。
const text = await store.get("hello.txt");const json = await store.get("config.json", { type: "json" });const buffer = await store.get("image.png", { type: "arrayBuffer" });const blob = await store.get("video.mp4", { type: "blob" });const stream = await store.get("large-file.zip", { type: "stream" });// 强一致性读取const fresh = await store.get("counter", { consistency: "strong" });
参数
| 参数 | 类型 | 必填 | 说明 |
| key | string | 是 | 对象的 Key。 |
| options.type | "text" | "json" | "arrayBuffer" | "blob" | "stream" | 否 | 返回值类型,默认 "text"。 |
| options.consistency | "eventual" | "strong" | 否 | 本次读取的一致性级别。 |
返回值
Promise<string | object | ArrayBuffer | Blob | ReadableStream | null>
十九、store.getWithHeaders(key, options?)
读取对象内容及其完整响应头。Key 不存在时返回 null。
const result = await store.getWithHeaders("document.pdf");// result.body — 对象内容// result.headers — 完整响应头(content-type, etag, cache-control 等)
参数
| 参数 | 类型 | 必填 | 说明 |
| key | string | 是 | 对象的 Key。 |
| options.consistency | "eventual" | "strong" | 否 | 本次读取的一致性级别。 |
返回值
Promise<{ body: string; headers: Record<string, string>;} | null>
20、store.delete(key)
删除一个对象。Key 不存在时不报错。
await store.delete("photos/cat.jpg");
参数
| 参数 | 类型 | 必填 | 说明 |
| key | string | 是 | 需要删除的 Key。 |
返回值
Promise<void>
21、store.list(options?)
列举命名空间中的对象。默认自动聚合所有分页。
// 列出所有对象const { blobs } = await store.list();// 按前缀过滤const { blobs } = await store.list({ prefix: "photos/" });// 按目录分组(只返回当前层级的文件和子目录)const { blobs, directories } = await store.list({ prefix: "photos/", directories: true,});// 强一致性const { blobs } = await store.list({ consistency: "strong" });// 手动分页const page1 = await store.list({ paginate: false });const page2 = await store.list({ paginate: false, cursor: page1.cursor });
参数
| 参数 | 类型 | 必填 | 说明 |
| options.prefix | string | 否 | 按 Key 前缀过滤。 |
| options.directories | boolean | 否 | 设为 true 时按 / 分组,返回 directories 字段。 |
| options.paginate | boolean | 否 | 设为 false 时返回单页结果(含 cursor)。 |
| options.cursor | string | 否 | 从上一次分页的 cursor 继续列举。 |
| options.consistency | "eventual" | "strong" | 否 | 本次列举的一致性级别。 |
返回值
{ blobs: Array<{ key: string; etag: string }>; directories?: string[]; // 仅 directories: true 时返回 cursor?: string; // 仅 paginate: false 时返回}
22、store.createUploadUrl(key, options?)
生成一个预签名的 PUT URL,允许浏览器或客户端直接把文件上传到 Blob,文件数据无需经过 Makers Functions 中转。适合大文件上传或对函数耗时敏感的场景,函数只负责签发一个几十字节的 URL,文件字节流直接从客户端流向 Blob。
const store = getStore("user-uploads");const { url, key, expiresAt } = await store.createUploadUrl("files/photo.jpg");
完整的客户端直传流程见下方客户端直传上传文件示例。
参数
| 参数 | 类型 | 必填 | 说明 |
| key | string | 是 | 上传后对象的 Key。 |
| options.expireSeconds | number | 否 | URL 有效期(秒),默认 3600。 |
| options.contentType | string | 否 | 若提供,客户端 PUT 时必须携带相同的 Content-Type 头,否则 403。 |
返回值
{ url: string; // 预签名 URL key: string; // 对象 Key expiresAt: number; // 过期时间(Unix 时间戳,单位秒)}
注意:生成的 URL 绑定 PUT 方法、指定的 Key、有效期窗口,以及 Content-Type(若设置)。任何不匹配的请求(其他方法、其他 Key、超出有效期、Content-Type 不一致)均返回 403。
23、listStores(options?)
列举当前项目下的所有命名空间。
import { listStores } from "@edgeone/pages-blob";// 在 Makers Functions 中const { stores } = await listStores();// 外部访问const { stores } = await listStores({ projectId: "pages-urtsvuwmfvli", token: "your-api-token",});
参数
| 参数 | 类型 | 必填 | 说明 |
| options.projectId | string | 外部访问时必填 | 目标项目 ID |
| options.token | string | 外部访问时必填 | API Token |
| options.consistency | "eventual" | "strong" | 否 | 读取一致性级别 |
返回值
{ stores: Array<{ name: string }>;}
25、客户端直传上传文件
通过预签名 URL 让浏览器直接把文件 PUT 到 Blob,函数只负责签发 URL,文件字节流不经过函数。适合大文件、批量上传等场景。
// 函数端:签发一次性上传 URLimport { getStore } from "@edgeone/pages-blob";export async function onRequest({ request }) { if (request.method !== "POST") { return new Response("Method not allowed", { status: 405 }); } const { name, contentType } = await request.json(); const store = getStore("user-uploads"); const { url, key, expiresAt } = await store.createUploadUrl( `uploads/${Date.now()}-${name}`, { expireSeconds: 3600, contentType: contentType || "application/octet-stream", } ); return new Response(JSON.stringify({ url, key, expiresAt }), { headers: { "Content-Type": "application/json" }, });}
// 浏览器端:取到 URL 后直接 PUT,无需 SDKasync function uploadFile(file) { // 1. 向函数申请上传 URL const { url, key } = await fetch("/api/get-upload-url", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: file.name, contentType: file.type }), }).then((r) => r.json()); // 2. 直接上传到 Blob await fetch(url, { method: "PUT", body: file, headers: { "Content-Type": file.type }, }); return key;}
26、按目录列出文件
import { getStore } from "@edgeone/pages-blob";export async function onRequest({ request }) { const store = getStore("user-uploads"); const url = new URL(request.url); const prefix = url.searchParams.get("path") || ""; const { blobs, directories } = await store.list({ prefix, directories: true, }); return new Response( JSON.stringify({ files: blobs, folders: directories }), { headers: { "Content-Type": "application/json" } } );}
27、示例模板
Blob SDK 用法示例:
预览地址
源码地址