在 Web 应用中处理用户上传的 Word 文档时,一个常见的需求是提取其中的纯文本内容。无论是用于内容检索、数据入库,还是作为预览功能的文本层,将 .docx 文件转换为纯文本都是一项基础能力。
传统做法依赖后端服务完成解析,但这也意味着文件需要离开用户的浏览器。对于涉及隐私或敏感内容的场景,这种方案往往不够理想。借助 WebAssembly,我们可以在浏览器本地完成这一过程。本文以 React 为例,梳理一套基于 WASM 的 Word 转文本实现思路。
适用场景与方案选择
在浏览器端解析 Word 文档,WebAssembly 是当前较为可行的路径。它将一个完整的文档处理内核编译为 .wasm 模块,在浏览器中运行,同时配合一个虚拟文件系统(VFS)来管理输入输出文件。
这种方案的特点在于:整个转换过程完全在本地完成,不需要将文件发送到服务器,也不依赖用户设备上安装 Microsoft Word。
当然,WASM 模块需要首次加载和编译,会带来一定的启动延迟。如果应用对首屏速度要求极高,需要权衡是否值得。
工程配置要点
在 React 项目中集成 WASM 模块,核心在于让主线程能够正确加载 .wasm 文件以及相关的运行时资源。通常需要将 spire.doc.js、spire.doc.wasm、spire.common.js、spire.common.wasm 以及 _framework 文件夹复制到 public 目录。
之所以放在 public 而非通过构建工具打包,是因为 .wasm 文件需要以原始方式被浏览器请求和实例化,走 Webpack 的动态导入流程反而可能引入路径解析问题。使用 /* webpackIgnore: true */ 注释跳过打包处理,可以让 spire.doc.js 直接从 public 目录加载。
字体文件的准备同样值得留意。WASM 运行环境默认不包含操作系统字体,如果文档中使用了 VFS 里缺失的字体,提取出的文本可能出现乱码或结构异常。将常用字体(如 Calibri.ttf)预先放入 public 目录,在初始化时加载到 VFS,是一个稳妥的做法。
第一步:加载 WASM 模块
在 React 组件中,加载模块的时机通常放在 useEffect 里,随组件挂载触发一次。加载完成后,将模块实例写入 window.wasmModule,并通过 useState 驱动按钮的可用状态。
javascript
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;
// 初始化 WASM,并修正 .wasm 文件的请求路径
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);
}
})();
}, []);
// ...
}
这里的两个细节值得说明。locateFile 回调用于告诉运行时去哪里加载 .wasm 文件,避免它按相对路径去找而落到错误的目录。window.wasmModule 这个挂载点则是为了后续在转换函数中直接取用,同时让运行时挂载的其他全局对象(如 window.spire、window.dotnetRuntime)保持可访问状态。
模块加载完成后,window.wasmModule.spiredoc、window.spire 与 window.dotnetRuntime 就可以在转换函数中使用了。
第二步:执行转换
转换过程可以概括为三个环节:准备文件、执行转换、输出结果。
准备阶段 需要将字体文件和待处理的 Word 文档通过 FetchFileToVFS 加载到虚拟文件系统中。字体进入 /Library/Fonts/ 目录,Word 文件放在根目录即可。
转换阶段 涉及 Document 类的实例化与加载。加载完成后,调用 SaveToFile 并指定 FileFormat.Txt,将文档以纯文本格式写入 VFS。
输出阶段 需要从 VFS 中读取生成的文件字节,包装为 Blob 对象,再通过 URL.createObjectURL 触发浏览器下载。
javascript
const WordToTXT = async () => {
const wasmModule = window.wasmModule.spiredoc;
if (!wasmModule) return;
// 加载字体到 VFS
await window.spire.FetchFileToVFS('CALIBRI.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
const inputFileName = 'Data.docx';
const outputFileName = 'WordToText.txt';
// 加载 Word 文件到 VFS
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);
// 实例化并加载文档
const doc = new wasmModule.Document();
doc.LoadFromFile(inputFileName);
// 保存为 TXT 格式
doc.SaveToFile({fileName: outputFileName, fileFormat: wasmModule.FileFormat.Txt});
// 从 VFS 读取结果并触发下载
const fileBytes = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileBytes], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
doc.Dispose();
};
把这段逻辑接回组件,按钮的禁用状态由 wasmModule 是否加载完成来决定:
javascript
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Convert Word to Plain Text in React</h1>
<button onClick={WordToTXT} disabled={!wasmModule}>
Convert
</button>
</div>
);
值得注意的约束与细节
段落与表格数量限制。免费版本的 Spire.Doc for JavaScript 对处理的文档规模存在限制:每个 Word 文档最多支持 500 个段落和 25 个表格。如果目标文档超出这个范围,转换结果可能不完整。这一限制在读取或写入时生效,属于功能层面的约束,而非性能问题。
资源释放 。在 WASM 环境中,Document 对象持有的非托管内存不受 JavaScript 垃圾回收的直接控制。显式调用 Dispose() 是释放这些资源的方式。虽然不调用未必立刻导致问题,但养成处理完毕后清理的习惯有助于避免长时间运行后的内存累积。
虚拟文件系统的内存属性。VFS 中的文件仅存在于当前页面会话期间,刷新页面后会重置。如果应用需要连续处理多个文档,注意输出文件名不要冲突,或在每次转换后清理临时文件。
编码问题 。SaveToFile 默认以 UTF-8 或系统编码写入文本。如果目标场景对编码有特定要求,可能需要在读取或写入环节做额外处理。对于中文内容,一般情况下 UTF-8 能够正常覆盖。
加载时机与用户体验 。WASM 的首次加载需要下载并编译二进制文件,可能耗时数百毫秒到数秒不等,具体取决于模块体积与网络状况。如果转换操作在用户点击时才触发首次加载,等待感会很明显。在组件挂载阶段就启动加载、并用 disabled 状态提示用户,是更平滑的做法。
小结
浏览器端的 Word 转文本,本质上是将原本在后端完成的文档解析任务迁移到了前端 WASM 运行时中。它的价值不在于"更强大",而在于"更私密"和"更即时的反馈"------用户不需要等待文件上传,敏感内容也不需要离开本地环境。
选择这条路时,需要接受 WASM 带来的启动成本,以及免费版本在文档规模上的硬性限制。如果项目的文档体量普遍较小、对隐私敏感、且能容忍首次加载的延迟,这套方案是值得考虑的。反之,如果文档规模较大且对处理速度有明确要求,后端解析仍然是更直接的路径。