在内容导出、报告生成、邮件合并等场景中,将富文本 HTML 内容转换为 Word 文档格式(.docx)是一项常见需求。将 HTML 内容直接输出为 Word 文档,可以让用户更方便地进行本地编辑和存档。本文介绍在 React 环境中,利用基于 WebAssembly 的方案将 HTML 文件或 HTML 字符串导入并生成 Word 文档的两种方式。
一、技术原理
该方案的核心是通过运行在浏览器中的 WASM 模块加载文档处理引擎。文档的加载、处理和保存均在虚拟文件系统(VFS) 中完成,不直接操作本地文件系统。
转换流程如下:
- 将字体文件加载到 VFS,确保文本正确渲染
- 从 VFS 读取 HTML 文件,或通过
Paragraph.AppendHTML()方法将 HTML 字符串追加到文档段落 - WASM 引擎将 HTML 内容转换为 Word 可识别的格式化内容
- 将文档保存为 .docx 格式并从 VFS 读取,触发浏览器下载
这一流程完全在客户端完成,文档内容无需上传至服务器。
二、环境配置
2.1 安装依赖包
在项目根目录执行以下命令:
bash
npm i spire.office
2.2 迁移运行时文件
安装完成后,将 node_modules/spire.office/lib 中的以下文件复制到 React 项目的 public 文件夹:
spire.doc.jsSpire.Doc.Wasm.zipspire.common.jsSpire.Common.Wasm.zip_framework文件夹
这些文件是 WASM 模块加载所必需的,放置在 public 目录下可以确保构建工具不会错误地处理它们。
2.3 准备字体资源
由于 WASM 环境不包含系统字体,如果 HTML 内容中使用了特定字体(如 Calibri 或中文字体),需要将对应的字体文件放入 public/static/font/ 目录,并通过 FetchFileToVFS 方法加载到 VFS 中。HTML 文件可放入 public/static/data/ 目录。
三、WASM 模块加载
以下代码展示了在 React 组件中异步加载 WASM 模块的方式,这是所有转换示例共用的前置步骤:
jsx
import React, { useState, useEffect } from 'react';
function App() {
const [wasmModule, setWasmModule] = useState(null);
useEffect(() => {
(async () => {
try {
const publicUrl = process.env.PUBLIC_URL || '';
const spireModule = await import(/* webpackIgnore: true */ `${publicUrl}/spire.doc.js`);
const rawModule = spireModule.default || spireModule;
window.wasmModule = typeof rawModule === 'function'
? await rawModule({ locateFile: p => p.endsWith('.wasm') ? `${publicUrl}/${p}` : p })
: rawModule;
setWasmModule(window.wasmModule);
} catch (error) {
console.error('Failed to load WASM module:', error);
}
})();
}, []);
// 转换函数将在后续定义
}
四、方式一:从 HTML 文件转换
这种方式适用于将已存在的 HTML 文件转换为 Word 文档。通过 Document.LoadFromFile() 方法直接加载 HTML 文件,并指定文件格式为 Html:
jsx
const convertHtmlFileToWord = async () => {
const wasmModule = window.wasmModule.spiredoc;
if (wasmModule) {
// 1. 加载字体到 VFS
await window.spire.FetchFileToVFS('CALIBRI.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
// 2. 加载 HTML 文件到 VFS
const inputFileName = 'sample1.html';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);
// 3. 创建 Document 实例并加载 HTML 文件
const doc = new wasmModule.Document();
doc.LoadFromFile({ fileName: inputFileName, fileFormat: wasmModule.FileFormat.Html, validationType: wasmModule.XHTMLValidationType.None });
// 4. 保存为 Word 文档
const outputFileName = 'HtmlToWord.docx';
doc.SaveToFile({ fileName: outputFileName, fileFormat: wasmModule.FileFormat.Docx });
// 5. 从 VFS 读取并下载
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const modifiedFile = new Blob([modifiedFileArray], { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" });
const url = URL.createObjectURL(modifiedFile);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
// 6. 释放资源
doc.Dispose();
};
五、方式二:从 HTML 字符串转换
这种方式适用于动态生成的 HTML 内容,例如从编辑器组件中获取用户输入的富文本。通过 Paragraph.AppendHTML() 方法将 HTML 字符串追加到文档段落中:
jsx
const convertHtmlStringToWord = async () => {
const wasmModule = window.wasmModule.spiredoc;
if (wasmModule) {
// 1. 加载字体到 VFS
await window.spire.FetchFileToVFS('CALIBRI.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
// 2. 准备 HTML 字符串
let HTML = "<html><head><title>HTML to Word Example</title><style>, body {font-family: 'Calibri';}, h1 {color: #FF5733; font-size: 24px; margin-bottom: 20px;}, p {color: #333333; font-size: 16px; margin-bottom: 10px;}";
HTML += "ul {list-style-type: disc; margin-left: 20px; margin-bottom: 15px;}, li {font-size: 14px; margin-bottom: 5px;}, table {border-collapse: collapse; width: 100%; margin-bottom: 20px;}";
HTML += "th, td {border: 1px solid #CCCCCC; padding: 8px; text-align: left;}, th {background-color: #F2F2F2; font-weight: bold;}, td {color: #0000FF;}</style></head>";
HTML += "<body><h1>This is a Heading</h1><p>This is a paragraph demonstrating the conversion of HTML to Word document.</p><p>Here's an example of an unordered list:</p><ul><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>";
HTML += "<p>Here's a table:</p><table><tr><th>Product</th><th>Quantity</th><th>Price</th></tr><tr><td>Jacket</td><td>30</td><td>$150</td></tr><tr><td>Sweater</td><td>25</td><td>$99</td></tr></table></body></html>";
// 3. 创建文档并添加内容
const doc = new wasmModule.Document();
let section = doc.AddSection();
let paragraph = section.AddParagraph();
paragraph.AppendHTML(HTML.toString('utf8', 0, HTML.length));
// 4. 保存为 Word 文档
const outputFileName = 'HtmlStringToWord.docx';
doc.SaveToFile({ fileName: outputFileName, fileFormat: wasmModule.FileFormat.Docx2016 });
// 5. 从 VFS 读取并下载
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const modifiedFile = new Blob([modifiedFileArray], { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" });
const url = URL.createObjectURL(modifiedFile);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
// 6. 释放资源
doc.Dispose();
};
六、两种方式对比
| 对比维度 | HTML 文件转换 | HTML 字符串转换 |
|---|---|---|
| 数据来源 | public/static/data/ 目录下的 .html 文件 |
JavaScript 中的字符串变量 |
| 加载方式 | Document.LoadFromFile() 直接加载 |
Paragraph.AppendHTML() 追加到段落 |
| 适用场景 | 静态 HTML 模板文件 | 动态生成的富文本内容 |
| 输入格式指定 | 需要指定 FileFormat.Html |
无需指定输入格式 |
| 验证控制 | 支持 XHTMLValidationType.None 跳过验证 |
无验证参数 |
七、关键方法与参数说明
| 方法/参数 | 说明 |
|---|---|
Document.LoadFromFile({ fileName, fileFormat, validationType }) |
从 VFS 加载指定格式的文件。fileFormat: FileFormat.Html 用于加载 HTML 文件 |
XHTMLValidationType.None |
加载 HTML 时跳过 XHTML 格式验证,可提高兼容性 |
Paragraph.AppendHTML(htmlString) |
将 HTML 字符串追加到段落中,转换为 Word 可识别的格式化内容 |
Document.AddSection() |
在文档中添加一个节(Section),用于承载段落内容 |
Section.AddParagraph() |
在节中添加一个段落(Paragraph),用于追加内容 |
Document.SaveToFile({ fileName, fileFormat }) |
将文档保存到 VFS 中,FileFormat.Docx 或 Docx2016 表示输出 .docx 格式 |
八、常见问题与建议
字体缺失导致样式异常:HTML 内容中通过 CSS 指定的字体若未在 VFS 中加载,可能出现字体回退或排版偏移。建议将常用字体(如 Calibri、Times New Roman)预先加载。
HTML 样式支持范围 :AppendHTML() 方法对内联样式和 <style> 标签中定义的 CSS 规则均有一定支持,但对于复杂 CSS 布局(如 Flex、Grid)或交互式脚本的转换效果有限。建议保持 HTML 结构相对简洁。
HTML 文件加载的验证问题 :当 HTML 文件格式不够严格时,可通过 validationType: XHTMLValidationType.None 跳过验证,避免加载失败。
资源释放 :转换完成后调用 doc.Dispose() 释放文档对象,避免内存泄漏。在批量处理场景中尤为重要。
WASM 模块加载状态:由于 WASM 模块加载需要时间,建议在 UI 中显示加载状态,并在模块未就绪时禁用操作按钮。
九、总结
本文介绍了在 React 应用中基于 WebAssembly 方案将 HTML 内容转换为 Word 文档的两种方式:从 HTML 文件转换和从 HTML 字符串转换。前者适用于静态模板文件,后者适用于动态生成的富文本内容。核心方法包括 Document.LoadFromFile() 加载 HTML 文件,以及 Paragraph.AppendHTML() 将 HTML 字符串导入文档段落。该方案在浏览器端完成所有处理,适用于将富文本内容导出为可编辑 Word 文档的场景。开发者可根据实际数据来源选择合适的方式,并注意字体加载和 HTML 样式兼容性问题。