表格做成 PDF 之后,数据就跟着版式一起被封住了。同一份资料发给十个人,收回来的是十份各自填过的 PDF;想把其中一份的内容落到别的模板上,只能对着屏幕一个字段一个字段地抄。表单域本身是有名字的,值也挂在名字下面,可只要离开阅读器,这层结构就取不出来。
本文用 Spire.PDF for JavaScript 把表单域里的值导出成数据文件,再把数据文件导回空白表单。ExportData 与 ImportData 都支持 Xml、Fdf、XFdf 三种格式——三者只是 DataFormat 的枚举差异,调用方式完全相同,区别仅在写出的文件结构。下面的代码以 XML 跑通全流程,FDF、XFDF 的写法并列在注释里,去掉注释即可切换。Spire.PDF for JavaScript 基于 WebAssembly 在浏览器端读写文档,全过程在本地完成,通过虚拟文件系统(VFS)读写文件,不需要后端配合。
本文介绍两个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.PDF for JavaScript。以下示例默认已安装 Spire.PDF 并完成 WebAssembly 模块初始化。
导出 PDF 表单数据
PdfFormWidget.ExportData 把表单域里的值写成一个数据文件,格式由第二个参数 DataFormat 指定。三种格式存的是同一份字段值,差别在文件结构:
| 数据格式 | 文件结构 |
|---|---|
DataFormat.Xml |
Adobe 表单数据 XML,字段名作元素名、取值作元素内容 |
DataFormat.Fdf |
表单数据格式(FDF),以 %FDF- 开头的文本结构,/T 存字段名、/V 存取值 |
DataFormat.XFdf |
XFDF,标准 XML,每个字段一个 <field name="…">,取值在 <value> 里 |
第三个参数是表单名,无名的 AcroForm 传空串即可。
function App() {
const exportFormData = async () => {
// 获取 Spire.PDF WASM 模块
const pdfModule = window.wasmModule?.spirepdf;
// 检查模块是否就绪
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// 将待导出的 PDF 文件载入 VFS
const inputFileName = '客户信息表.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// 创建 PdfDocument 对象并加载 PDF 文档
const doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// 用文档的表单句柄构造 PdfFormWidget,才能访问数据导出接口
const formWidget = new pdfModule.PdfFormWidget(doc.Form.H);
// 本次演示导出 XML
const dataFiles = [
{ fileName: '表单数据.xml', format: pdfModule.DataFormat.Xml },
// { fileName: '表单数据.fdf', format: pdfModule.DataFormat.Fdf },
// { fileName: '表单数据.xfdf', format: pdfModule.DataFormat.XFdf },
];
for (const item of dataFiles) {
// 第三个参数是表单名,无名表单传空串
formWidget.ExportData(item.fileName, item.format, '');
}
doc.Close();
// 从 VFS 读取生成的文件,触发下载
for (const item of dataFiles) {
const fileArray = window.dotnetRuntime.Module.FS.readFile(item.fileName);
const blob = new Blob([fileArray], { type: 'application/octet-stream' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = item.fileName;
a.click();
URL.revokeObjectURL(url);
}
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>导出表单数据</h1>
<button onClick={exportFormData}>
开始导出
</button>
</div>
);
}
export default App;
导出的 XML 表单数据文件:

导入 PDF 表单数据
PdfFormWidget.ImportData 读入数据文件,按字段名把值写回表单域;第二个参数 DataFormat 只决定解析方式,与文件扩展名无关,三种格式通用。
导入的对象是空白表单。模板留空分发,回收到数据文件后再挨个填回去,字段多的时候不必重新录一遍。
function App() {
const importFormData = async () => {
// 获取 Spire.PDF WASM 模块
const pdfModule = window.wasmModule?.spirepdf;
// 检查模块是否就绪
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// 将待回填的空白表单载入 VFS
const inputFileName = '空白客户信息表.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// 本次演示回填 XML 数据文件
const dataFiles = [
{ fileName: '表单数据.xml', format: pdfModule.DataFormat.Xml, outputFileName: '导入XML数据.pdf' },
// { fileName: '表单数据.fdf', format: pdfModule.DataFormat.Fdf, outputFileName: '导入FDF数据.pdf' },
// { fileName: '表单数据.xfdf', format: pdfModule.DataFormat.XFdf, outputFileName: '导入XFDF数据.pdf' },
];
for (const item of dataFiles) {
// 数据文件同样要先载入 VFS
await window.spire.FetchFileToVFS(item.fileName, "", `${process.env.PUBLIC_URL}/data/`);
const doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// 读入数据文件,把值按字段名写回表单域
const formWidget = new pdfModule.PdfFormWidget(doc.Form.H);
formWidget.ImportData(item.fileName, item.format);
doc.SaveToFile(item.outputFileName);
doc.Close();
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(item.outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = item.outputFileName;
a.click();
URL.revokeObjectURL(url);
}
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>导入表单数据</h1>
<button onClick={importFormData}>
开始导入
</button>
</div>
);
}
export default App;
XML 数据导入后回填完成的表单:

常见问题
导入后部分字段还是空的
原因:导入按字段名匹配,数据文件里的名字与表单域名称必须完全一致(区分大小写与空格)。对不上的字段会被直接跳过,既不报错也不返回提示,只有匹配上的字段被填值。
解决:先遍历字段集合打印出真实名称,再用它核对数据文件:
const fields = formWidget.FieldsWidget;
for (let i = 0; i < fields.Count; i++) {
console.log(fields.get_Item({ index: i }).Name);
}
三种数据格式该怎么选
原因:三者存的字段值相同,差别在结构与工具支持。Fdf 体积最小、以 %FDF- 开头,适合只在表单程序之间传递;XFdf 和 Xml 都是 XML,可以直接打开查看、也能用文本工具比对差异,跨工具交换更稳;Xml 的字段名就是元素名,结构最直白。
解决:程序内部往返用 Fdf;要进版本库、要人工检查或对接其他系统用 XFdf;只需要一份可读的字段名与取值清单,用 Xml。
导入时报 Xml_MessageWithErrorPosition 或 "not a valid FDF file"
原因:ImportData 按第二个参数指定的格式解析文件,不看扩展名。内容与格式不符时第一步就失败:XML 报 Xml_MessageWithErrorPosition, Xml_InvalidRootData,FDF 报 The source is not a valid FDF file because it does not start with "%FDF-"。
解决:按文件真实的格式传入对应的 DataFormat;数据文件用导出的原件,不要改完之后另存成别的格式。
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







