首页
/ ToolJet Generate File 动作详解:动态构造 CSV、Text、PDF 文件并触发下载

ToolJet Generate File 动作详解:动态构造 CSV、Text、PDF 文件并触发下载

2026-09-05 13:11:32作者:凌朦慧Richard

Generate file 是 ToolJet 内置的一种客户端动作(Action),用于在运行时根据动态数据即时构造文件并直接触发浏览器下载,典型场景包括"一键导出表格数据为 CSV"、"生成纯文本报告"、"以 PDF 表格形式输出查询结果"等。本文基于官方文档 generate-file.md 的完整配置说明展开,并结合 前端事件调度实现文件生成核心库CSV 序列化实现,讲清楚每种文件类型对 Data 字段的格式要求、默认值行为以及底层生成原理,读完后可直接在 App Builder 和 RunJS 中正确使用该动作。

动作定位与执行链路

从官方文档描述看,该动作"允许你在运行时构造文件并让用户下载"。在前端源码中,它对应事件类型 generate-file,与 runQueryshowAlertgoToApp 等并列注册为可在 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);
}

这段代码揭示了三个源码级事实:

  1. 所有字段都经过变量解析datafileNamefileType 均通过 getResolvedValue 解析,因此支持 {{...}} 动态变量引用;
  2. 存在隐式默认值:Data 为空时回退为 [],File name 为空时回退为 data.txt,Type 为空时回退为 csv
  3. 文本类型的内部标识是 plaintext 而非 text:虽然 UI 选项表中 Type 写作 CSVTextPDF,但序列化映射表的键是 csv / plaintext / pdf,这一点在 RunJS 中调用时尤为关键(见下文)。

配置项全解

官方文档给出了如下四个选项,本文逐一补充其在源码中的落地行为:

Option 说明 源码层面的行为
Type 要生成的文件类型:CSVTextPDF 内部取值为 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.jsgeneratePDF 函数中清晰可见:

  • 传入字符串:生成纯文本 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.jsgenerateFile 主函数处理 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/csv MIME 类型,其余(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 调用与事件面板配置走的是同一条执行链路,行为完全一致。

使用要点小结

  1. 参数命名与文档一致fileNamefileTypedata 三个字段都支持 {{...}} 变量表达式,可引用查询结果、组件数据或全局变量;
  2. 文本类型务必传字符串:UI 中 Type 选择 Text 后,内部类型值是 plaintext;若数据是对象数组,请在 Data 字段中显式 JSON.stringify
  3. CSV 列头来自对象的键:确保数组中各对象键集一致,列头顺序即对象键的顺序;
  4. PDF 想要表格效果就传对象数组,想要纯文本就传字符串;
  5. 善用 Debounce:在高频触发的事件上配置毫秒级延迟,避免重复下载。

以上配置与行为均可在当前仓库中直接查证:文档主体见 generate-file.md,事件分发见 eventsSlice.js,文件生成核心见 generate-file.jsgenerate-csv.js,RunJS 调用说明见 run-action-from-runjs.md

登录后查看全文
热门项目推荐
相关项目推荐