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
本篇技术指南围绕 ToolJet 的Generate file(生成文件)动作展开,讲解如何在应用构建器中通过事件处理器动态构造文件(CSV、Text、PDF)并让用户即时下载。读者学完后,将能熟练配置该动作的四个核心选项(Type、File name、Data、Debounce),掌握三种文件格式对应的 Data 数据结构,并结合仓库源码理解其底层实现原理(Blob 下载、jsPDF 渲染、PapaParse 序列化),以及如何从 RunJS 中触发该动作,为报表导出、数据快照、单据生成等内部工具场景提供开箱即用的文件输出能力。
Generate file 动作是什么
Generate file 是 ToolJet 内置的客户端动作(Action)之一,其官方定位是:允许你在应用运行时动态构造文件,并让用户直接下载("This action allows you to construct files on the fly and let users download it")。
它不需要任何后端接口配合,所有文件的构造与下载均在浏览器端完成,适合在以下场景使用:
- 将 Table 组件的当前页数据导出为 CSV 供用户下载;
- 将表单内容或日志拼接为纯文本文件;
- 将结构化数据渲染为带表格样式的 PDF 报告。
从源码结构看,该动作被注册在前端事件处理核心中:frontend/src/AppBuilder/_stores/constants/actions.js 的合法动作列表中,并在 frontend/src/AppBuilder/_stores/slices/eventsSlice.js 的generate-file分支中被解析执行,最终调用 frontend/src/_lib/generate-file.js 完成文件的构造与下载。
动作选项说明
在事件处理器的动作配置面板中选择Generate file后,需要配置以下选项:
| 选项 | 说明 |
|---|---|
| Type | 要生成的文件类型,可选:CSV、Text和PDF |
| File name | 生成文件的名称(含或不含扩展名均可,下载时会按此命名) |
| Data | 用于构造文件的数据,其格式取决于文件类型,详见下文各格式说明 |
| Debounce | 默认为空,可填入数值指定延迟执行的毫秒数,例如300 |
其中Debounce(防抖)选项用于控制动作执行的延迟:留空表示立即执行;填入如300表示在触发事件后等待 300 毫秒再执行,常用于避免高频事件(如输入框变化、按钮连续点击)导致文件被重复生成。
CSV 文件格式
Data 结构要求
要生成CSV文件,Data 字段需要传入一个对象数组。ToolJet 假定数组中每个对象的键(key)一致,这些键会被作为 CSV 文件的列头。
官方示例:
{{ [ { name: 'John', email: 'john@tooljet.com' }, { name: 'Sarah', email: 'sarah@tooljet.com' }, ] }}以上代码生成的 CSV 文件内容为:
name,email John,john@tooljet.com Sarah,sarah@tooljet.com底层实现:PapaParse 序列化
从源码实现看,CSV 的构造并非手写拼接字符串,而是通过 frontend/src/_lib/generate-csv.js 调用 PapaParse 库的Papa.unparse(records)完成:
import Papa from 'papaparse'; export default function generateCSV(records) { return Papa.unparse(records); }Papa.unparse会自动从对象数组中提取键名作为表头,并将每个对象的键值按序输出为一行,这与官方文档描述的"键即列头"行为完全一致。同时它负责处理转义(如字段中含逗号、引号、换行时),因此即便数据中包含特殊字符也能生成合法 CSV。
该序列化函数被接入事件处理链:frontend/src/AppBuilder/_stores/slices/eventsSlice.js 中根据文件类型选择转换函数,其中csv: generateCSV。
实战:导出 Table 组件数据
最典型的应用是把表格数据导出为 CSV。ToolJet 表格组件自身的"导出数据"功能就复用了同一套机制——frontend/src/AppBuilder/Widgets/NewTable/_utils/exportData.js 中将表格数据经generateFile(fileName, csvString, 'csv')下载。这意味着你在 Generate file 动作中同样可以直接引用表格数据,例如将 Data 字段填写为:
{{ components.table1.currentPageData }}或配合查询结果:
{{ queries.getUsers.data }}Text 文件格式
要生成Text文件,Data 字段需要传入一个字符串(string)。
如果你的数据源是对象数组(例如来自 Table 组件),则需要先将其序列化为字符串再传入。官方给出的经典写法是,直接利用表格组件当前页数据:
{{ JSON.stringify(components.table1.currentPageData) }}JSON.stringify会把对象数组转换为 JSON 文本,随后 Text 类型会将其原样写入文件。
从事件处理源码看,Text 分支的转换函数是恒等函数(原样返回):
plaintext: (plaintext) => plaintext,即字符串数据不做任何转换直接进入文件内容(见 frontend/src/AppBuilder/_stores/slices/eventsSlice.js)。
PDF 文件格式
Data 结构要求
PDF格式支持两种输入类型:
- 字符串(string):生成的 PDF 内容为纯文本;
- 对象数组(array of objects):生成的 PDF 将以表格形式展示数据,数组中的对象键作为列名,值作为单元格内容。
底层实现:jsPDF + autoTable
PDF 的生成由 frontend/src/_lib/generate-file.js 中的generatePDF函数完成,其核心逻辑为:
- 动态引入
jspdf库(await import('jspdf')),创建jsPDF文档实例; - 根据数据类型分派处理(
processValue):- 字符串:调用
doc.text(value, x, y, { align: 'left', maxWidth: ... })逐行写入纯文本; - 对象数组:提取
Object.keys(value[0])作为列头,调用doc.autoTable({ head, body })渲染表格,并依据doc.lastAutoTable.finalY推进后续内容的纵向坐标; - 单个对象:同样以键为列头、值为一行数据渲染为表格;
- 其他类型:抛出
Invalid data type. Expected string, object, or array.错误;
- 字符串:调用
- 最后调用
doc.save(filename)触发下载。
可以看到,数组 → 表格、字符串 → 纯文本的行为与官方文档描述完全对应,且渲染时已处理分页(通过pageWidth、margin、maxWidth约束文本宽度,避免内容溢出页面)。
实战:将查询结果导出为 PDF 报告
例如 Data 字段填写查询返回的对象数组:
{{ queries.getOrders.data }}生成后的 PDF 会自动按对象的键渲染出带列头的表格,非常适合订单清单、用户列表等报表类场景。
文件下载的浏览器实现
无论是 CSV 还是 Text,最终下载都通过 frontend/src/_lib/generate-file.js 完成:
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); }关键机制说明:
- 数据先包装为
Blob,CSV 使用text/csvMIME 类型,Text 使用text/plain; - 兼容旧版 IE/Edge 的
msSaveOrOpenBlob分支; - 现代浏览器走"创建临时
<a>元素 +URL.createObjectURL+click()触发下载 +revokeObjectURL释放内存"的标准流程。
该函数同时被事件动作与表格组件导出功能复用,是整个文件下载能力的单一实现点。
默认值与容错行为
从事件处理源码可以看到该动作的内置默认值(frontend/src/AppBuilder/_stores/slices/eventsSlice.js):
const data = getResolvedValue(event.data, customVariables, moduleId) || []; const fileName = getResolvedValue(event.fileName, customVariables, moduleId) || 'data.txt'; const fileType = getResolvedValue(event.fileType, customVariables, moduleId) || 'csv';即:未填 Data 时按空数组处理、未填文件名时默认data.txt、未填类型时默认csv。所有字段均支持模板表达式({{ }}),getResolvedValue会在执行时解析其中的变量与查询引用,因此文件内容、文件名都可以随应用状态动态变化。
通过 RunJS 触发 Generate file
官方文档提示:Generate file 动作也可以脱离事件面板、通过RunJS代码方式运行。这一能力源于事件处理核心将generateFile暴露进了动作集合(frontend/src/AppBuilder/_stores/slices/eventsSlice.js),因此你在 RunJS 中可以用事件派发的方式触发该动作,从而实现"读取外部数据后程序化生成文件"的自动化流程。
这种方式适合在复杂的条件逻辑(如数据校验通过后才导出、按批次拆分导出等)中使用,可结合 RunJS 的完整 JavaScript 运行时编写判断分支。
使用建议与注意事项
- CSV 列一致性:对象数组要求所有对象键一致,若不同对象的键不同,生成的 CSV 会出现列错位或缺失,建议先对数据做归一化(如使用
map映射出固定字段)。 - Text 数据必须为字符串:对象/数组需要先用
JSON.stringify序列化,否则会被当作无效输入。 - PDF 大表格:
autoTable会自动分页,但超大数据集可能影响生成速度,建议导出前在查询层做分页或筛选(例如使用pageSize、limit参数)。 - Debounce 的应用:当动作绑定在输入框的 onChange 等高频事件上时,设置
300左右的防抖值可避免频繁弹窗下载。 - 文件名动态化:File name 支持模板表达式,例如
{{ 'orders-' + Date.now() + '.csv' }}可生成带时间戳的唯一文件名。
以上内容基于 ToolJet 仓库 docs/versioned_docs/version-3.0.0-LTS/actions/generate-file.md 整理,并结合 frontend/src/_lib/generate-file.js、frontend/src/_lib/generate-csv.js 与 frontend/src/AppBuilder/_stores/slices/eventsSlice.js 等源码文件交叉印证,读者可沿这些路径深入阅读完整实现。
【免费下载链接】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),仅供参考