Firebase JS SDK 中 FileData 接口深度解析:在 Gemini 多模态请求中引用 Google Cloud Storage 文件
Firebase JS SDK 中 FileData 接口深度解析:在 Gemini 多模态请求中引用 Google Cloud Storage 文件
本文以 Firebase AI Web SDK(@firebase/ai,位于本仓库 packages/ai)自动生成的 API 参考文档 ai.filedata.md 为骨架,深入讲解 FileData 接口的定义、两个核心属性(fileUri 与 mimeType)的语义,以及它在 Gemini 多模态内容请求(generateContent、countTokens 等)中与 FileDataPart、Part 联合类型之间的协作关系。读完本文,你将掌握如何构造一个合法的 FileData 对象、如何在请求中把它作为内容的一部分发送,以及它与 base64 内联数据(InlineDataPart)在实现层面的本质区别。
FileData 接口:指向云端文件的引用
根据 ai.filedata.md 的定义,FileData 是一个描述“指向 Google Cloud Storage 上已上传文件的引用数据”的接口。它不携带文件二进制内容本身,而是通过一个 gs:// 风格的 URI 指向云端对象,让模型在推理时按需读取。
接口签名(与源码一致):
export interface FileData
其 TypeScript 声明位于 packages/ai/src/types/content.ts:
/**
* Data pointing to a file uploaded on Google Cloud Storage.
* @public
*/
export interface FileData {
mimeType: string;
fileUri: string;
}
它被标记为 @public,属于 SDK 对外公开的稳定 API,开发者可以直接在请求构造中使用。
属性详解:fileUri 与 mimeType
FileData 只有两个属性,二者均为必填的 string,原文档以属性表的形式给出了完整签名:
| 属性 | 类型 | 说明 |
|---|---|---|
fileUri |
string |
指向 Google Cloud Storage 上文件的 URI |
mimeType |
string |
文件资源的 MIME 类型 |
fileUri:文件在云端的位置
fileUri 表示被引用文件在 Google Cloud Storage 上的地址。从仓库测试代码可以看出其实际取值形式:
- packages/ai/src/requests/request-helpers.test.ts 使用
'gs://sample.appspot.com/image.jpeg'; - packages/ai/src/requests/response-helpers.test.ts 使用
'gs://bucket/file'; - packages/ai/integration/count-tokens.test.ts 展示了从 Firebase 配置中动态拼接 URI 的写法:
gs://${FIREBASE_CONFIG.storageBucket}/images/tree.png,即把 Firebase 项目的storageBucket作为 bucket 名、以images/tree.png作为对象路径。
可以推断,fileUri 的标准格式为 gs://<bucket-name>/<object-path>,对象需事先通过 Cloud Storage 上传,并满足模型后端对该文件的访问权限要求。
mimeType:声明文件资源的媒体类型
mimeType 用于告知模型该文件的媒体类型,以便模型按正确的模态(图像、音频、文档等)处理。仓库测试中出现的真实取值包括:
image/jpeg(request-helpers.test.ts);image/png(count-tokens.test.ts);application/pdf(response-helpers.test.ts)。
在 Live 会话场景中还会出现 audio/pcm 等音频类型(见 packages/ai/integration/live.test.ts)。实际可支持的具体媒体类型取决于所选后端(Gemini Developer API 或 Agent Platform Gemini API)的能力,建议按被引用文件的真实媒体格式填写。
FileData 在内容体系中的位置:Part 联合类型与 FileDataPart
FileData 本身只是数据载体,真正把它送入请求的是封装它的内容部件 FileDataPart。在 packages/ai/src/types/content.ts 中定义如下:
/**
* Content part interface if the part represents {@link FileData}
* @public
*/
export interface FileDataPart {
type: 'fileData';
fileData: FileData;
thought?: boolean;
}
FileDataPart 通过 type: 'fileData' 判别字段与 Part 联合类型建立联系。Part 在同一文件中定义(content.ts),涵盖 TextPart、InlineDataPart、FunctionCallPart、FunctionResponsePart、FileDataPart、ExecutableCodePart 与 CodeExecutionResultPart 共七种部件:
export type Part =
| TextPart
| InlineDataPart
| FunctionCallPart
| FunctionResponsePart
| FileDataPart
| ExecutableCodePart
| CodeExecutionResultPart;
对应的 API 参考文档可参见 FileDataPart 接口。此外,content.ts 中的 UnknownPart 也包含可选的 fileData?: FileData 字段,用于表示来自模型 wire API 或调用方提供的、尚未打上 type 标签的原始部件——这正是 SDK 内部做类型归一化的输入形态。
底层实现:请求格式化与类型归一化
理解 FileData 如何“跑通”一次请求,需要看两个关键实现。
assignPartType:给未打标签的 fileData 分配类型
在 packages/ai/src/requests/response-helpers.ts 中,assignPartType(part) 负责把 Part | UnknownPart 归一化为带 type 判别字段的 Part:
} else if (part.fileData !== undefined) {
return { ...part, type: 'fileData' } as FileDataPart;
}
即只要部件对象中存在 fileData 字段,就会被自动标记为 'fileData' 类型。该函数被 createEnhancedContentResponse 用于逐条处理响应候选中的 parts(response-helpers.ts),相关单元测试见 response-helpers.test.ts,其中断言 { fileData: { mimeType: 'application/pdf', fileUri: 'gs://bucket/file' } } 被正确判定为 type === 'fileData'。
formatGenerateContentInput:把 FileData 部件放进请求
在请求侧,formatGenerateContentInput 会把用户提供的输入(含 fileData 部件)规整为后端可接受的 contents<a href="https://link.gitcode.com/i/cea1a7afb00b4828b00d3e6960b782b8" target="_blank">].parts[] 结构。测试 [request-helpers.test.ts 给出了完整行为:
const result = formatGenerateContentInput([
'What is this?',
{
type: 'fileData',
fileData: {
mimeType: 'image/jpeg',
fileUri: 'gs://sample.appspot.com/image.jpeg'
}
}
]);
// 结果为:
// {
// contents: [{
// role: 'user',
// parts: [
// { type: 'text', text: 'What is this?' },
// {
// type: 'fileData',
// fileData: {
// mimeType: 'image/jpeg',
// fileUri: 'gs://sample.appspot.com/image.jpeg'
// }
// }
// ]
// }]
// }
可以看到,FileData 对象原样保留在 fileData 字段内,SDK 只负责将其放入正确的 parts 数组,不改变其属性结构。文本与文件部件可以混合出现在同一次请求的 parts 中,实现“看图提问”式调用。
实战示例:在 generateContent 与 countTokens 中使用 FileData
综合上述源码与测试,一次完整的引用云存储文件的调用可以这样组织(基于 count-tokens.test.ts 的结构,同样适用于 generateContent):
import { getAI, getGenerativeModel, GoogleAIBackend } from 'firebase/ai';
import { initializeApp } from 'firebase/app';
const app = initializeApp({ /* 你的 Firebase 配置 */ });
const ai = getAI(app, { backend: new GoogleAIBackend() });
const model = getGenerativeModel(ai, { model: 'gemini-2.0-flash' });
const filePart = {
type: 'fileData',
fileData: {
mimeType: 'image/png',
fileUri: 'gs://your-project.appspot.com/images/tree.png'
}
};
const response = await model.generateContent(['描述这张图片的内容', filePart]);
const text = response.text();
在 token 计数场景中,FileDataPart 同样可以直接作为请求内容传入:
const response = await model.countTokens([filePart]);
集成测试对同一 fileUri 断言了 promptTokensDetails<a href="https://link.gitcode.com/i/c2968f302ef58cd4a7d5eb557901687c" target="_blank">0].modality 为 Modality.IMAGE,说明后端会依据 mimeType 将文件识别为对应模态参与计费与处理([count-tokens.test.ts)。同时测试注释明确指出,基于公开存储引用的该用例“不保证在 Google AI 后端通过”(count-tokens.test.ts),即此用法以 Agent Platform Gemini API(原 Vertex AI Gemini API)为预期环境,实际接入时需结合你所选后端的文件访问能力验证。
FileData 与 InlineDataPart:两种文件传递方式的取舍
在同一内容体系里,还有另一种携带媒体数据的部件:InlineDataPart,其数据载体是 GenerativeContentBlob(定义于 content.ts):
export interface GenerativeContentBlob {
mimeType: string;
/** Image as a base64 string. */
data: string;
}
两者的对比如下:
| 维度 | FileData(云存储引用) | InlineDataPart(base64 内联) |
|---|---|---|
| 数据存放位置 | Google Cloud Storage,请求中只携带 URI | 请求体中携带 base64 编码的二进制数据 |
| 属性结构 | fileUri + mimeType |
inlineData.mimeType + inlineData.data |
| 适用场景 | 大文件、可复用对象、已托管在云端的资源 | 小体积、即时生成、无需持久化的数据 |
| 请求体积 | 小(仅引用信息) | 随数据量线性增长 |
集成测试 count-tokens.test.ts 展示了内联图片部件的构造方式:inlineData: { mimeType: IMAGE_MIME_TYPE, data: TINY_IMG_BASE64 }。当文件已经上传到 Cloud Storage 时,优先使用 FileData 可以显著减小请求体并复用已有对象存储能力;当数据来自内存或临时生成时,InlineDataPart 更直接。
使用建议与注意事项
- 两个字段都必须提供:
fileUri与mimeType均为必填字符串,缺一不可,且mimeType应如实反映文件内容,以免后端按错误模态处理。 - URI 采用
gs://协议:参照测试中的gs://${storageBucket}/...写法,可直接基于 Firebase 项目的storageBucket构造,也可使用 Cloud Storage 控制台给出的对象 URI。 - 关注后端差异:本仓库的集成测试表明,云端文件引用相关的行为在不同后端(
BackendType.AGENT_PLATFORM与BackendType.GOOGLE_AI)下可能不一致(count-tokens.test.ts),接入前建议针对目标后端做一次端到端验证。 - 源码即规范:接口的最终权威定义始终以 packages/ai/src/types/content.ts 为准,API 参考文档(ai.filedata.md、ai.filedatapart.md)由 SDK 团队从源码自动生成,两者保持一致。