在 React 中用 JavaScript 将 Word 转换为文本

在 Web 应用中处理用户上传的 Word 文档时,一个常见的需求是提取其中的纯文本内容。无论是用于内容检索、数据入库,还是作为预览功能的文本层,将 .docx 文件转换为纯文本都是一项基础能力。

传统做法依赖后端服务完成解析,但这也意味着文件需要离开用户的浏览器。对于涉及隐私或敏感内容的场景,这种方案往往不够理想。借助 WebAssembly,我们可以在浏览器本地完成这一过程。本文以 React 为例,梳理一套基于 WASM 的 Word 转文本实现思路。

适用场景与方案选择

在浏览器端解析 Word 文档,WebAssembly 是当前较为可行的路径。它将一个完整的文档处理内核编译为 .wasm 模块,在浏览器中运行,同时配合一个虚拟文件系统(VFS)来管理输入输出文件。

这种方案的特点在于:整个转换过程完全在本地完成,不需要将文件发送到服务器,也不依赖用户设备上安装 Microsoft Word。

当然,WASM 模块需要首次加载和编译,会带来一定的启动延迟。如果应用对首屏速度要求极高,需要权衡是否值得。

工程配置要点

在 React 项目中集成 WASM 模块,核心在于让主线程能够正确加载 .wasm 文件以及相关的运行时资源。通常需要将 spire.doc.jsspire.doc.wasmspire.common.jsspire.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.spirewindow.dotnetRuntime)保持可访问状态。

模块加载完成后,window.wasmModule.spiredocwindow.spirewindow.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 带来的启动成本,以及免费版本在文档规模上的硬性限制。如果项目的文档体量普遍较小、对隐私敏感、且能容忍首次加载的延迟,这套方案是值得考虑的。反之,如果文档规模较大且对处理速度有明确要求,后端解析仍然是更直接的路径。

相关推荐
计算机魔术师2 小时前
马斯克称 Grok 4.7 使 xAI 在智能体编码领域位列第三
前端
摸鱼仙人~2 小时前
前端秋招异步手写题系统刷题路线:从 Promise 到 Pipeline、LazyMan 与并发控制
前端
秋天的一阵风2 小时前
⚡上线 24 小时,13% 的付费团队连夜换到 Jev:它到底什么来头?
前端·人工智能·ai编程
程序员若风2 小时前
CSRF攻击原理介绍和利用-腾讯云开发者社区
前端·网络·安全·web安全·网络安全·csrf攻击·腾讯云开发者社区
风尘小子2 小时前
node.js系列:process配置
前端·node.js
恋猫de小郭2 小时前
AndroidX 新增 Security State ,支持可编程的系统安全状态查询
android·前端·flutter
IT_陈寒3 小时前
Redis大key删除引发的服务雪崩,这次我真记住了
前端·人工智能·后端
李少兄3 小时前
深入解析 JavaScript 中的 `document` 对象
javascript
IMPYLH3 小时前
HTML 的 <style> 元素
前端·html