ToolJet Generate File 动作详解:动态构造 CSV、Text、PDF 文件并触发下载
【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet
Generate file 是 ToolJet 内置的一种客户端动作(Action),用于在运行时根据动态数据即时构造文件并直接触发浏览器下载,典型场景包括"一键导出表格数据为 CSV"、"生成纯文本报告"、"以 PDF 表格形式输出查询结果"等。本文基于官方文档 generate-file.md 的完整配置说明展开,并结合 前端事件调度实现、文件生成核心库 与 CSV 序列化实现,讲清楚每种文件类型对 Data 字段的格式要求、默认值行为以及底层生成原理,读完后可直接在 App Builder 和 RunJS 中正确使用该动作。
动作定位与执行链路
从官方文档描述看,该动作"允许你在运行时构造文件并让用户下载"。在前端源码中,它对应事件类型generate-file,与runQuery、showAlert、goToApp等并列注册为可在 RunJS 中调用的动作之一(见 actions.js 中的动作清单)。
事件分发逻辑位于 eventsSlice.js:
case 'generate-file': { const data = getResolvedValue(event.data, customVariables, moduleId) || []; const fileName = getResolvedValue(event.fileName, customVariables, moduleId) || 'data.txt'; const fileType = getResolvedValue(event.fileType, customVariables, moduleId) || 'csv'; const fileData = { csv: generateCSV, plaintext: (plaintext) => plaintext, pdf: (pdfData) => pdfData, }fileType; return generateFile(fileName, fileData, fileType); }这段代码揭示了三个源码级事实:
- 所有字段都经过变量解析:
data、fileName、fileType均通过getResolvedValue解析,因此支持{{...}}动态变量引用; - 存在隐式默认值:Data 为空时回退为
[],File name 为空时回退为data.txt,Type 为空时回退为csv; - 文本类型的内部标识是
plaintext而非text:虽然 UI 选项表中 Type 写作CSV、Text、PDF,但序列化映射表的键是csv/plaintext/pdf,这一点在 RunJS 中调用时尤为关键(见下文)。
配置项全解
官方文档给出了如下四个选项,本文逐一补充其在源码中的落地行为:
| Option | 说明 | 源码层面的行为 |
|---|---|---|
| Type | 要生成的文件类型:CSV、Text、PDF | 内部取值为csv/plaintext/pdf,决定 Data 的序列化方式;未填时默认为csv |
| File name | 生成文件的名称 | 未填时默认为data.txt,支持变量插值 |
| Data | 用于构造文件的数据,格式随文件类型而定 | 为空时默认为[],支持{{...}}动态表达式 |
| Debounce | 默认空;填入数字表示该毫秒数后才执行动作,如300 | 事件节流配置,避免高频事件(如连续输入)反复触发文件下载 |
其中 Debounce 字段对交互密集型场景很有价值:例如在输入事件上挂载 generate file 时,填入300可使动作在用户停止触发 300ms 后才真正执行一次,避免产生大量重复下载。
三种文件类型的数据格式要求
CSV:对象数组,键即列头
使用CSV格式时,Data 字段应是一个对象数组,ToolJet 假定每个对象的键都相同,并将这些键作为 CSV 的列头。官方文档示例:
{{ [ { name: 'John', email: 'john@tooljet.com' }, { name: 'Sarah', email: 'sarah@tooljet.com' }, ] }}生成结果为:
name,email John,john@tooljet.com Sarah,sarah@tooljet.com这一行为由 generate-csv.js 实现,仅 5 行核心逻辑:
import Papa from 'papaparse'; export default function generateCSV(records) { return Papa.unparse(records); }即底层直接委托给 papaparse 的Papa.unparse完成"对象数组 → CSV 文本"的序列化。这意味着字段中包含逗号、引号、换行符等字符时会按 RFC 4180 标准自动加引号转义,可直接粘贴到 Excel 等工具中解析。
Text:字符串;对象数组需先序列化
使用Text格式时,Data 字段应直接是一个字符串。如果数据源是对象数组(例如表格组件的当前页数据),必须先stringify再传入 Data 字段。官方文档给出的示例是:
{{JSON.stringify(components.table1.currentPageData)}}对应源码中plaintext的序列化函数是恒等函数(plaintext) => plaintext,即不做任何转换、原样写入文件——这也解释了为什么必须自行完成序列化:若直接传对象数组,Blob会得到无意义的[object Object]内容。
PDF:字符串或对象数组,二选一
PDF 支持两种输入形态,行为差异在 generate-file.js 的generatePDF函数中清晰可见:
- 传入字符串:生成纯文本 PDF。源码中以
doc.text(value, x, y, { align: 'left', maxWidth: pageWidth - 2 * margin })逐行绘制,页边距margin = 10; - 传入对象数组:生成表格形态的 PDF。源码以数组第一个元素的键作为表头,调用 jspdf-autotable 的
doc.autoTable({ head: [columnNames], body: value.map((item) => Object.values(item)) })渲染出带行列的表格,并依据doc.lastAutoTable.finalY控制后续内容的起始位置; - 传入单个对象:同样渲染为表格,只是 body 只有一行数据;
- 其他类型(数字、布尔等):抛出
Invalid data type. Expected string, object, or array.错误。
底层生成机制:Blob 下载与 PDF 动态加载
generate-file.js 的generateFile主函数处理 CSV 与文本文件的下载:
const type = fileType === 'csv' ? 'text/csv' : 'text/plain'; const blob = new Blob([data], { type }); if (window.navigator.msSaveOrOpenBlob) { window.navigator.msSaveBlob(blob, filename); } else { const elem = window.document.createElement('a'); elem.href = window.URL.createObjectURL(blob); elem.download = filename; document.body.appendChild(elem); elem.click(); document.body.removeChild(elem); window.URL.revokeObjectURL(elem.href); }要点:
- CSV 使用
text/csvMIME 类型,其余(plaintext)使用text/plain; - 兼容旧版 IE/Edge 的
msSaveOrOpenBlob接口,现代浏览器走标准的"创建<a>元素 +URL.createObjectURL+ 模拟点击"流程,并在使用后revokeObjectURL释放内存,避免 Blob URL 泄漏; - 全程在浏览器端完成,数据不经过服务端中转,适合包含敏感数据的本地导出场景。
PDF 则采用动态 import按需加载(await import('jspdf'),见 generate-file.js L22-L26),jspdf 体积较大,按需引入可避免首屏加载负担;同时对 ESM/CJS 两种导出形式都做了兼容取值(jsPDFNamespace.jsPDF || jsPDFNamespace.default)。
通过 RunJS 调用 generate file
除在组件事件(Events)中配置外,该动作同样可在 RunJS 代码中调用,官方文档在 run-action-from-runjs.md 中给出了签名与示例:
actions.generateFile('<fileName>', '<fileType>', '<data>')三个实战示例(均出自该文档):
// 以表格当前页数据生成 CSV 文件 csvfile1 actions.generateFile('csvfile1', 'csv', '{{components.table1.currentPageData}}') // 以字符串化后的表格数据生成文本文件 textfile1 actions.generateFile('textfile1', 'plaintext', '{{JSON.stringify(components.table1.currentPageData)}}') // 以表格当前页数据生成 PDF 表格文件 Pdffile1 actions.generateFile('Pdffile1', 'pdf', '{{components.table1.currentPageData}}')RunJS 桥接实现位于 eventsSlice.js L1298-L1310:内部先校验三个参数均非空(缺失时弹出Action failed: fileName, fileType and data are required错误提示),再构造actionId: 'generate-file'的事件对象并复用与 UI 完全相同的executeAction执行通道——这意味着 RunJS 调用与事件面板配置走的是同一条执行链路,行为完全一致。
使用要点小结
- 参数命名与文档一致:
fileName、fileType、data三个字段都支持{{...}}变量表达式,可引用查询结果、组件数据或全局变量; - 文本类型务必传字符串:UI 中 Type 选择
Text后,内部类型值是plaintext;若数据是对象数组,请在 Data 字段中显式JSON.stringify; - CSV 列头来自对象的键:确保数组中各对象键集一致,列头顺序即对象键的顺序;
- PDF 想要表格效果就传对象数组,想要纯文本就传字符串;
- 善用 Debounce:在高频触发的事件上配置毫秒级延迟,避免重复下载。
以上配置与行为均可在当前仓库中直接查证:文档主体见 generate-file.md,事件分发见 eventsSlice.js,文件生成核心见 generate-file.js 与 generate-csv.js,RunJS 调用说明见 run-action-from-runjs.md。
【免费下载链接】ToolJetOpen-source foundation of ToolJet AI - the enterprise app generation platform for internal tools, dashboards, business applications, workflows and AI agents. Build visually, from a prompt, or from Claude Code, Codex and Cursor over MCP 🚀项目地址: https://gitcode.com/GitHub_Trending/to/ToolJet
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考