本文是 Agent SDK 中对象存储 Blob(对象存储)模块的参考。您可以在服务端读写文件、图片、报告和 JSON 等对象,按 key 组织与检索对象,并为浏览器签发直传上传凭据。本文先给出一段可直接运行的最短示例,随后说明对象 key 的约束、各方法的用途与调用方式、浏览器直传和错误处理。
Blob(对象存储)为您的智能体提供在云端读写非结构化对象的能力。无论是持久化生成的文件、保存中间产物,还是为前端签发上传链接,您都可以通过一致的方法接口完成,无需自行管理底层存储资源。
与会话存储、工具、沙箱不同,对象存储是一个独立的包 @iga-pages/blob,而非核心包 @iga-pages/agent 的子路径导出。使用以下方式导入 blob:
import { blob } from "@iga-pages/blob";
blob 导入后可直接使用,导入本身不触发任何运行时初始化;首次调用具体方法时,SDK 才会换取项目身份并建立底层连接。所有 Blob 调用都在 IGA Pages 服务端进行;浏览器需要读写文件时,由应用服务端校验权限后代为调用,或通过下文的直传凭据完成。
在调用 Blob 方法前,请确认您已完成以下准备:
Blob 的身份模型与会话存储一致:SDK 会使用当前应用的运行时身份访问对应项目中的资源,项目身份在运行时自动确定,您无需在业务代码或环境变量中传入。
导入 blob 后,即可写入和读取内容。下面这段代码是使用对象存储的最短闭环:写入一个文本对象,再用相同的 key 读回它。
import { blob } from "@iga-pages/blob"; await blob.set("reports/daily/report.md", "# 分析报告"); const report = await blob.get("reports/daily/report.md"); console.log(report); // "# 分析报告"
写入与读取都以对象 key 为标识,无需提前创建目录或初始化存储资源。确认这一最短闭环可用后,再按下文展开更完整的读写、列举与直传能力。
key 是对象在当前项目内的名称,例如 reports/daily/report.md。后续请求使用相同的 key 即可访问该对象;底层存储按项目隔离,您无需关心物理路径或存储桶。
可以用 / 组织 key,无需提前创建目录。例如,reports/daily/ 可作为前缀,用于筛选一组报告。key 不是下载地址。
await blob.set("reports/daily/report.md", "# 每日报告"); const reports = await blob.list({ prefix: "reports/daily/" }); console.log(reports.blobs);
key 不能为空,不能以 / 开头或结尾,也不能包含反斜杠、空字符(NUL)、连续的 /,或单独的 .、.. 路径段。返回的 key 保留完整前缀,可直接用于读取、查询和删除。
在查阅方法之前,先了解 Blob 读写涉及的核心对象与类型。
BlobObject(对象元信息)字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
key | string | 对象键(在存储桶内的路径标识)。 |
etag | string? | 对象内容标识,可用于校验版本。 |
size | number? | 对象字节大小。 |
contentType | string? | 对象的内容类型(MIME)。 |
lastModified | string? | 最近修改时间。 |
写入正文类型 BlobBody 可取:string、ArrayBuffer、ArrayBufferView、Blob、ReadableStream<Uint8Array>。
读取格式 BlobReadFormat 可取:text、json、arrayBuffer、blob、stream;缺省为 text。
BlobStore 公开以下方法。具体参数、参数位置和返回类型,请查看对应方法签名:在编辑器中悬停方法名,或跳转到类型定义即可查看。
| 方法 | 用途 |
|---|---|
blob.set() | 写入文本、二进制内容或数据流 |
blob.setJSON() | 将数据保存为 JSON |
blob.get() | 读取对象内容 |
blob.info() | 查询对象信息 |
blob.list() | 列出对象,支持按 key 前缀筛选 |
blob.delete() | 删除对象 |
blob.createUploadUrl() | 生成临时上传凭据,供浏览器直接上传文件 |
各方法异步返回结果,失败时抛出异常,处理方式见请求与错误处理。createUploadUrl() 的使用示例见浏览器直接上传。
使用 blob.set() 保存文本、二进制内容或数据流。默认覆盖相同 key 的对象。
const object = await blob.set("reports/daily/report.md", "# 分析报告", { contentType: "text/markdown", }); console.log(object.key);
写入成功后返回对象信息(BlobObject)。contentType 是可选的 MIME 类型,明确传入时原样交给底层存储。需要避免覆盖已有对象时,设置 onlyIfNew: true;同名对象存在时不会被覆盖,调用会抛出 BlobError。
使用 blob.setJSON() 保存可序列化为 JSON 的数据,无需手动转换为字符串或指定内容类型。
await blob.setJSON("reports/daily/result.json", { total: 5050, });
默认覆盖相同 key 的对象,也可通过 onlyIfNew: true 限制为仅新建。成功后返回对象信息;数据无法序列化为 JSON 时抛出 BlobError(code 为 BadRequest)。
使用 blob.get() 读取内容,默认返回文本;对象不存在时返回 null。
const report = await blob.get("reports/daily/report.md"); if (report === null) { console.log("报告不存在"); } else { console.log(report); }
读取 JSON 时指定 format: "json",并可用泛型标注返回结构:
const result = await blob.get<{ total: number }>("reports/daily/result.json", { format: "json", }); console.log(result?.total); // 5050
还支持 arrayBuffer、blob 和 stream 读取格式。JSON 内容无效时会抛出 BlobError(code 为 BadRequest);泛型只提供 TypeScript 类型提示,不校验数据的业务结构。浏览器需要读取文件时,由应用服务端校验权限、调用 get(),再通过应用接口返回内容。
使用 blob.info() 查询对象信息,不读取正文;对象不存在时返回 null。
const object = await blob.info("reports/daily/report.md"); if (object !== null) { console.log(object.key, object.size); }
返回信息包含 key,以及可选的 size(字节数)、contentType、etag 和 lastModified。
使用 blob.list() 列出当前项目的对象,可通过 prefix 按 key 前缀筛选。对象列表位于返回结果的 blobs 中。
const result = await blob.list({ prefix: "reports/daily/" }); for (const object of result.blobs) { console.log(object.key); }
默认自动读取全部分页。需要逐页读取时,设置 paginate: false,并用返回的 cursor 获取下一页:
const page = await blob.list({ prefix: "reports/daily/", paginate: false, }); if (page.cursor) { const nextPage = await blob.list({ prefix: "reports/daily/", paginate: false, cursor: page.cursor, }); console.log(nextPage.blobs); }
没有 cursor 时表示没有下一页。翻页时保持 prefix 和 directories 选项一致。需要按目录浏览时,设置 directories: true:直接子目录汇总在 directories 中,目录内的文件不再展开到 blobs。
使用 blob.delete() 删除对象:
await blob.delete("reports/daily/report.md");
删除成功后无返回数据。对象不存在或重复删除时也会成功;删除失败时抛出异常。
使用 blob.createUploadUrl() 在服务端生成临时上传凭据,再由浏览器直接上传文件内容,避免大文件经过应用服务器中转。
服务端先校验用户权限并确定允许写入的 key,再生成凭据:
import { randomUUID } from "node:crypto"; import { blob } from "@iga-pages/blob"; export async function createUploadTicket() { return blob.createUploadUrl(`uploads/${randomUUID()}.bin`, { onlyIfNew: true, }); }
凭据(BlobUploadTicket)包含 key、url、method 和 expiresAt。其中 method 固定为 "PUT",expiresAt 是以秒为单位的 Unix 时间戳。将凭据返回给浏览器,并在有效期内上传:
type UploadTicket = { key: string; url: string; method: "PUT"; expiresAt: number; }; export async function uploadFile(ticket: UploadTicket, file: File) { const response = await fetch(ticket.url, { method: ticket.method, body: file, }); if (!response.ok) { throw new Error(`上传失败:${response.status}`); } return ticket.key; }
生成凭据不代表上传完成,应以浏览器上传请求成功为准。保存返回的 key,供后续读取文件。凭据过期后,重新向服务端申请。
默认允许覆盖相同 key 的对象;示例通过 onlyIfNew: true 避免覆盖。
取消浏览器上传时,在该次 fetch() 中传入 signal。
Blob 方法直接返回结果,失败时抛出异常。使用 try...catch 捕获 BlobError,其结构为 { code, status?, message }:
import { blob, BlobError } from "@iga-pages/blob"; export async function saveOnce(key: string, content: string) { try { return await blob.set(key, content, { onlyIfNew: true }); } catch (error) { if (error instanceof BlobError) { console.error(error.toJSON()); } throw error; } }
code 是字符串错误码,可能来自 SDK 本地校验、网络层或上游服务;不要假设所有值都属于同一组 HTTP 分类。status 仅在 SDK 能取得 HTTP 状态时出现。
各模块的错误处理方式不同。处理 Blob 错误时,先用 instanceof BlobError 判断,再读取其字段。
对象不存在时,get() / info() 返回 null、delete() 视为成功,不会抛出 BlobError。
需要取消操作或限制等待时间时,通过请求选项传入 abortSignal:
const report = await blob.get("reports/daily/report.md", { abortSignal: AbortSignal.timeout(10_000), });
取消会停止当前操作的等待、后续分页和响应流读取,不会回滚已经完成的写入。