Blob
概述
EdgeOne Pages Blob 是面向 Pages Functions 的分布式对象存储,适合存储图片、文档、用户上传文件等非结构化数据。默认提供最终一致的快速读取,写入后短暂时间内可读到最新值;如需立即读到最新值,可在单次读取时切换为强一致模式。
注意:
免费版套餐下单账户存储容量为 1GB。
当前提供 Node.js SDK(
@edgeone/pages-blob),其他运行时的 SDK 正在开发中。Blob 面向 Pages Functions 的运行时数据需要(如读写、查询、加工),不建议作为公网图床或 CDN 使用。
工作原理
数据访问
Blob 数据持久化在云端的对象存储中,并通过边缘节点加速读取。读取请求由离用户最近的边缘节点响应,毫秒级返回。。
一致性模型
Blob 提供两种一致性模式,可按需选择:
模式 | 读取行为 | 适用场景 |
最终一致(默认) | 走边缘缓存,速度最快;新写入的数据需要短暂时间(通常秒级)才能在所有节点上读到 | 内容展示、缓存读取、容忍短暂不一致的业务 |
强一致 | 跳过缓存直读主存储,保证读到最新值;读取耗时略高 | 计数器、状态机、必须立即读到最新写入值的业务 |
调用
store.get(key) 时默认使用最终一致;传入 { consistency: "strong" } 即切换到强一致。也可在 getStore 时统一指定整个 Store 的默认一致性。注意:
强一致性会增加读取耗时,仅在确有必要时使用。
使用场景
用户文件上传
接收用户上传的头像、附件、图片等文件,存储到 Blob 中按用户或业务维度组织。
AI 生成内容
存储 AI 模型生成的图片、文档、报告,按批次或类型目录管理,方便检索和展示。
结构化数据集
将多个 JSON 文件按目录组织存放,通过
list 遍历处理整个数据集。快速开始
1. 安装 SDK
npm install @edgeone/pages-blob
2. 在 Pages Functions 中使用
import { getStore } from "@edgeone/pages-blob";export async function onRequest({ request }) {const store = getStore("my-store");// 写入await store.set("hello.txt", "Hello, EdgeOne Pages!");const content = await store.get("hello.txt");return new Response(content);}
首次调用
getStore("my-store") 时,平台会自动为当前项目创建名为 my-store 的命名空间,后续调用直接使用已有的命名空间。3. 部署
部署 Pages 项目后,触发一次请求即可在控制台的 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 })在 Pages Functions 中
只需指定命名空间名称:
const store = getStore("my-store");// 也可使用对象形式,便于同时指定默认一致性const store = getStore({ name: "my-store", consistency: "strong" });
在 Pages Functions 之外访问(例如本地脚本、外部服务)
需要额外提供项目 ID 和 API Token:
const store = getStore({name: "my-store",projectId: "pages-urtsvuwmfvli",token: "your-api-token",});
参数
参数 | 类型 | 必填 | 说明 |
name | string | 是 | 命名空间名称 |
projectId | string | 在 Pages Functions 之外访问时必填 | 目标项目 ID |
token | string | 在 Pages 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 不存在时写入await store.set("init.json", data, { onlyIfNew: true });
参数
参数 | 类型 | 必填 | 说明 |
key | string | 是 | 对象的 Key |
value | string | ArrayBuffer | Blob | ReadableStream | 是 | 对象内容 |
options.onlyIfNew | boolean | 否 | 设为 true 时仅在 Key 不存在时写入 |
返回值
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 的数据 |
options.onlyIfNew | boolean | 否 | 设为 true 时仅在 Key 不存在时写入 |
返回值
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>
store.delete(key)
删除一个对象。Key 不存在时不报错。
await store.delete("photos/cat.jpg");
参数
参数 | 类型 | 必填 | 说明 |
key | string | 是 | 需要删除的 Key |
返回值
Promise<void>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 时返回}
listStores(options?)
列举当前项目下的所有命名空间。
import { listStores } from "@edgeone/pages-blob";// 在 Pages 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 }>;}
示例
按目录列出文件
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" } });}
条件写入(防覆盖)
import { getStore } from "@edgeone/pages-blob";export async function onRequest({ request }) {const store = getStore("configs");// 仅在 Key 不存在时写入,避免覆盖已有数据await store.setJSON("app/settings", { version: 1 }, { onlyIfNew: true });const settings = await store.get("app/settings", { type: "json" });return new Response(JSON.stringify(settings), {headers: { "Content-Type": "application/json" },});}