关于 fileset-api
fileset-api 是面向 Domo 自定义应用开发者的 FileSets 测试版 API 指南。它介绍如何通过 Domo 文件 API 操作文件、管理容器,并对文件内容执行语义查询。
主要功能
- 01上传和下载文件
- 02浏览、搜索并分页列出文件
- 03读取文件元数据并删除文件
- 04创建和搜索 FileSets
- 05用自然语言查询文件内容
适用场景
- 01在 Domo 自定义应用中构建文件浏览器
- 02将生成的 CSV 文件上传到 FileSet
- 03按目录或名称查找文件
- 04用自然语言查询报告内容
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ stahura/ domo-ai-vibe-rules --skill fileset-api安装后在 Claude Code 中输入/fileset-api确认已加载。
详细介绍
Domo FileSets — App Framework API
状态: 此 API 处于 BETA 阶段,可能会发生变更。请采取防御性编程方式——使用 try/catch 包装调用,并向用户显示有意义的错误信息。
FileSets 是 Domo 的文件存储系统。在自定义应用中,你可以上传和下载文件、浏览目录树、按名称搜索,以及针对文件内容运行 AI 驱动的语义查询。
设置 — 无需配置清单
与数据集不同,fileset 不需要在 manifest.json 中添加条目。只需在服务文件中将 fileset ID 定义为常量:
// src/services/api.js
const FILESET_ID = 'b6ebf7e9-64ae-4e6d-b8ca-b356fe62923f'; // replace with your fileset ID
可从 Domo UI 获取 fileset ID,也可通过 fileset-cli 技能获取(filesets search --name "...")。
URL 模式:/api/ 与 /domo/
在 Domo 自定义应用中,有两种有效的方式:
| 模式 | 适用场景 |
|---|---|
fetch('/api/files/v1/...') | 最简单。适用于所有 JSON 和二进制调用。无需导入。 |
domo.get('/domo/files/v1/...') | 需要使用 domo.js 代理时使用(例如已在其他位置使用 domo.*)。仅支持 JSON,不支持二进制。 |
以下示例使用 fetch('/api/...'),因为它可以一致地处理 JSON 和二进制(上传/下载)。无论如何,二进制操作都必须使用 fetch。
文件
列出 fileset 中的文件
const response = await fetch(`/api/files/v1/filesets/${FILESET_ID}/files/search`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
fieldSort: [{ field: 'created', order: 'DESC' }],
filters: [],
dateFilters: []
})
});
const data = await response.json();
const files = data.files; // array of file objects
按目录和名称筛选:
const response = await fetch(
`/api/files/v1/filesets/${FILESET_ID}/files/search` +
`?directoryPath=/reports&immediateChildren=true&limit=50`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
fieldSort: [{ field: 'name', order: 'ASC' }],
filters: [{ field: 'name', value: ['.pdf'], operator: 'LIKE' }],
dateFilters: []
})
}
);
使用 data.pageContext.next 中的 next 令牌进行分页:
// data.pageContext shape: { next, offset, limit, total }
if (data.pageContext.next) {
const nextPage = await fetch(
`/api/files/v1/filesets/${FILESET_ID}/files/search?next=${data.pageContext.next}`,
{ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}) }
);
}
获取文件元数据
// By file ID
const response = await fetch(`/api/files/v1/filesets/${FILESET_ID}/files/${fileId}`);
const file = await response.json();
// { id, path, name, fileType, contentType, size, hash, created, createdBy }
// By path (when you know the directory structure)
const response = await fetch(
`/api/files/v1/filesets/${FILESET_ID}/path?path=${encodeURIComponent('/reports/march.pdf')}`
);
下载文件
使用 fetch 并创建临时下载链接。下载端点会返回二进制内容:
async function downloadFile(fileId, filename) {
const response = await fetch(
`/api/files/v1/filesets/${FILESET_ID}/files/${fileId}/download`
);
if (!response.ok) throw new Error(`Download failed: ${response.status}`);
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
对于基于文本的文件,也可以使用 .text() 而不是 .blob(),直接读取内容:
const response = await fetch(`/api/files/v1/filesets/${FILESET_ID}/files/${fileId}/download`);
const text = await response.text(); // use for .txt, .csv, .md, etc.
上传文件
使用包含两个部分的 FormData——二进制文件和目录元数据。不要设置 Content-Type;浏览器会自动使用正确的 multipart 边界:
async function uploadFile(file, directoryPath = '/') {
const formData = new FormData();
formData.append('file', file); // File or Blob
formData.append('createFileRequest', JSON.stringify({ directoryPath })); // metadata
const response = await fetch(`/api/files/v1/filesets/${FILESET_ID}/files`, {
method: 'POST',
body: formData
// ⚠️ Do NOT set Content-Type header — browser handles it
});
if (!response.ok) throw new Error(`Upload failed: ${response.status}`);
return response.json(); // returns the new file object
}
从文件输入元素上传:
document.getElementById('file-input').addEventListener('change', async (e) => {
const file = e.target.files[0];
const record = await uploadFile(file, '/uploads/2024');
console.log('Uploaded:', record.id, record.path);
});
从生成的 Blob 上传(例如 CSV 导出):
const csv = 'name,value\nAlice,100\nBob,200';
const blob = new Blob([csv], { type: 'text/csv' });
const file = new File([blob], 'export.csv', { type: 'text/csv' });
await uploadFile(file, '/exports');
删除文件
// By file ID
await fetch(`/api/files/v1/filesets/${FILESET_ID}/files/${fileId}`, { method: 'DELETE' });
// By path
await fetch(
`/api/files/v1/filesets/${FILESET_ID}/path?path=${encodeURIComponent('/reports/march.pdf')}`,
{ method: 'DELETE' }
);
FileSets(容器)
你可以在应用中创建和管理 fileset 容器,不过通常 fileset 已经存在,应用只需使用硬编码的 ID。
搜索 fileset
const response = await fetch('/api/files/v1/filesets/search?limit=50', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
fieldSort: [{ field: 'updated', order: 'DESC' }],
filters: [{ field: 'name', value: ['reports'], operator: 'LIKE' }],
dateFilters: []
})
});
创建 fileset
const response = await fetch('/api/files/v1/filesets', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Monthly Reports', description: 'Automated PDF outputs' })
});
const fileset = await response.json();
const filesetId = fileset.id;
AI 驱动的文件查询
需要在 fileset 上启用 aiEnabled: true。此功能会针对文件内容运行自然语言问题,并返回带有相关性评分的排序结果:
const response = await fetch(`/api/files/v1/filesets/${FILESET_ID}/query`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: 'What were the key revenue drivers last quarter?',
directoryPath: '/reports/2024',
topK: 5
})
});
const data = await response.json();
// data.matches: [{ id, node: { file object }, score: 0.89 }, ...]
文件对象结构
{
"id": "xyz789-file-id",
"path": "/reports/2024/march.pdf",
"name": "march.pdf",
"fileType": "PDF",
"contentType": "application/pdf",
"size": 204800,
"hash": "sha256:abc...",
"hashAlgorithm": "SHA256",
"created": "2024-03-01T08:00:00Z",
"createdBy": 12345
}
完整示例:文件浏览器服务
适用于 React 应用的简洁服务模块模式:
// src/services/filesetApi.js
const FILESET_ID = 'your-fileset-id-here';
const BASE = `/api/files/v1/filesets/${FILESET_ID}`;
export async function listFiles(directoryPath = null, nameFilter = null) {
const params = new URLSearchParams({ limit: '100' });
if (directoryPath) params.set('directoryPath', directoryPath);
const filters = nameFilter
? [{ field: 'name', value: [nameFilter], operator: 'LIKE' }]
: [];
const response = await fetch(`${BASE}/files/search?${params}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ fieldSort: [{ field: 'name', order: 'ASC' }], filters, dateFilters: [] })
});
if (!response.ok) throw new Error(`List failed: ${response.status}`);
const data = await response.json();
return data.files ?? [];
}
export async function downloadFileContent(fileId) {
const response = await fetch(`${BASE}/files/${fileId}/download`);
if (!response.ok) throw new Error(`Download failed: ${response.status}`);
return response.text(); // swap for .blob() for binary files
}
export async function uploadFile(file, directoryPath = '/') {
const form = new FormData();
form.append('file', file);
form.append('createFileRequest', JSON.stringify({ directoryPath }));
const response = await fetch(`${BASE}/files`, { method: 'POST', body: form });
if (!response.ok) throw new Error(`Upload failed: ${response.status}`);
return response.json();
}
export async function queryFiles(question, directoryPath = null, topK = 5) {
const body = { query: question, topK };
if (directoryPath) body.directoryPath = directoryPath;
const response = await fetch(`${BASE}/query`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
if (!response.ok) throw new Error(`Query failed: ${response.status}`);
const data = await response.json();
return data.matches ?? [];
}
通过 CLI 管理 fileset
若要发现 fileset ID、浏览文件、将文件下载到磁盘,或在应用外运行命令,请使用 fileset-cli 技能。