React 中 Word 文档转图片的实践方案

将 Word 文档转换为 JPG 或 PNG 图片,在内容预览、社交分享、网页嵌入等场景中有较广泛的应用。与 PDF 转换类似,在浏览器端直接完成转换可以避免文档上传至服务器,减少传输过程中的数据暴露风险。本文介绍一种基于 WebAssembly 技术的纯前端转换方案,说明在 React 环境中如何将 Word 文档的每一页导出为图片文件。

一、技术原理

该方案的核心是一个运行在浏览器中的 WASM(WebAssembly)模块,它将文档处理引擎编译为浏览器可直接执行的二进制代码。由于 WASM 运行在沙箱环境中,无法直接访问本地文件系统,因此通过虚拟文件系统(VFS) 来管理文件读写。

转换流程如下:

  1. 将字体文件和 Word 文档加载到 VFS 中
  2. Document 对象从 VFS 读取源文件,逐页转换为图片流
  3. 从 VFS 读取生成的图片数据,通过浏览器 API 触发下载

这一架构使得文档处理完全在客户端完成,无需依赖后端服务。

二、环境配置

2.1 安装依赖包

在项目根目录执行以下命令:

bash 复制代码
npm i spire.office

2.2 迁移运行时文件

安装完成后,将 node_modules/spire.office/lib 中的以下文件复制到 React 项目的 public 文件夹:

  • spire.doc.js
  • Spire.Doc.Wasm.zip
  • spire.common.js
  • Spire.Common.Wasm.zip
  • _framework 文件夹

这些文件是 WASM 模块运行所必需的资源,放置在 public 目录下可以确保构建工具不会错误地处理它们。

2.3 准备字体和文档资源

由于 WASM 环境不包含系统字体,如果文档中使用了特定字体(如 Times New Roman 或中文字体),需要将对应的字体文件放入 public/static/font/ 目录,并通过 FetchFileToVFS 方法加载到 VFS 中。测试用的 Word 文档可放入 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);
      }
    })();
  }, []);

  // 转换函数将在后续定义
}

四、将 Word 转换为 JPG

Document.SaveImageToStreams() 方法可以将 Word 文档的指定页面转换为图片流。以下示例展示了如何逐页转换并触发下载:

jsx 复制代码
const convertWord = async () => {
    // 获取 WASM 模块中的 Spire.Doc 对象
    const wasmModule = window.wasmModule.spiredoc;
    if (wasmModule) {
      // 将字体文件加载到虚拟文件系统(VFS)中
      await window.spire.FetchFileToVFS('times.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
      await window.spire.FetchFileToVFS('timesbd.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
      await window.spire.FetchFileToVFS('timesbi.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
      await window.spire.FetchFileToVFS('timesi.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);

      // 获取输入文件并添加到虚拟文件系统
      const inputFileName = 'input.docx';
      await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);

      // 创建文档对象
      const doc = new wasmModule.Document();

      // 加载 Word 文件
      doc.LoadFromFile(inputFileName);

      // 获取文档总页数
      const totalPages = doc.GetPageCount();

      // 遍历每一页,转换为图片
      for (let pageIndex = 0; pageIndex < totalPages; pageIndex++) {

        // 将指定页面转换为图片流
        let img = doc.SaveImageToStreams({ pageIndex, type: wasmModule.ImageType.Bitmap });

        // 根据页码指定输出文件名
        const outputFileName = `IMG-${pageIndex}.jpg`;

        // 保存图片流为 JPG 文件
        img.Save(outputFileName);

        // 从虚拟文件系统读取保存的文件
        const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
        const modifiedFile = new Blob([modifiedFileArray], { type: 'image/jpeg' });

        // 创建下载链接
        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);
      }
      // 释放资源
      doc.Dispose();
    }
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>在 React 中将 Word 转换为 JPG</h1>
      <button onClick={convertWord} disabled={!wasmModule}>
        转换
      </button>
    </div>
  );
}

export default App;

五、转换为 PNG 格式

将输出文件的扩展名和 Blob 的 MIME 类型从 image/jpeg 改为 image/png,即可输出 PNG 格式图片。其他转换逻辑保持一致:

jsx 复制代码
const convertWord = async () => {
    // 获取 WASM 模块中的 Spire.Doc 对象
    const wasmModule = window.wasmModule.spiredoc;
    if (wasmModule) {
      // 将字体文件加载到虚拟文件系统(VFS)中
      await window.spire.FetchFileToVFS('times.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
      await window.spire.FetchFileToVFS('timesbd.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
      await window.spire.FetchFileToVFS('timesbi.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
      await window.spire.FetchFileToVFS('timesi.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);

      // 获取输入文件并添加到虚拟文件系统
      const inputFileName = 'input.docx';
      await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);

      // 创建文档对象
      const doc = new wasmModule.Document();

      // 加载 Word 文件
      doc.LoadFromFile(inputFileName);

      // 获取文档总页数
      const totalPages = doc.GetPageCount();

      // 遍历每一页,转换为图片
      for (let pageIndex = 0; pageIndex < totalPages; pageIndex++) {

        // 将指定页面转换为图片流
        let img = doc.SaveImageToStreams({ pageIndex, type: wasmModule.ImageType.Bitmap });

        // 根据页码指定输出文件名
        const outputFileName = `IMG-${pageIndex}.png`;

        // 保存图片流为 PNG 文件
        img.Save(outputFileName);

        // 从虚拟文件系统读取保存的文件
        const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
        const modifiedFile = new Blob([modifiedFileArray], { type: 'image/png' });

        // 创建下载链接
        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);
      }
      // 释放资源
      doc.Dispose();
    }
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>在 React 中将 Word 转换为 PNG</h1>
      <button onClick={convertWord} disabled={!wasmModule}>
        转换
      </button>
    </div>
  );
}

export default App;

六、关键参数说明

参数/方法 说明
SaveImageToStreams({ pageIndex, type }) 将指定页面转换为图片流。pageIndex 从 0 开始计数
ImageType.Bitmap 输出位图格式,可进一步保存为 JPG 或 PNG
GetPageCount() 获取文档的总页数,用于循环遍历
img.Save(fileName) 将图片流保存为文件到 VFS 中
FS.readFile(fileName) 从 VFS 中读取文件二进制数据

七、常见问题与注意事项

字体缺失导致乱码:如果文档使用了特殊字体而 VFS 中未加载对应字体,生成的图片可能出现文字错位或乱码。建议将所有用到的字体文件预先加载到 VFS 中。

多页文档的处理SaveImageToStreams 每次只能转换一页,需要通过 GetPageCount() 获取总页数后循环处理。对于页数较多的文档,可考虑添加进度提示以改善用户体验。

图片格式选择:JPG 格式使用有损压缩,文件体积较小,适合包含大量图片的文档;PNG 格式使用无损压缩,适合包含文字和图表的文档,清晰度更高。

资源释放 :转换完成后调用 doc.Dispose() 释放文档对象,避免内存泄漏。在处理大型文档时尤为重要。

WASM 加载问题 :如遇到模块加载失败,请检查 public 目录下的运行时文件是否完整,以及 locateFile 路径配置是否正确。可利用浏览器开发者工具的 Network 面板进行排查。

八、总结

本文介绍了在 React 应用中利用基于 WebAssembly 的文档处理方案,将 Word 文档逐页转换为 JPG 和 PNG 图片的方法。核心在于通过 Document.SaveImageToStreams() 方法将页面转换为图片流,再保存为图片文件并触发下载。该方案在浏览器端完成所有处理,无需将文档上传至服务器,适用于对数据隐私有要求的应用场景。开发者可根据实际需要选择 JPG 或 PNG 格式,并注意处理好文档中的字体资源。

相关推荐
Csvn12 分钟前
异步并发控制:手写并发池、竞态防护与取消的完整方案
前端·javascript
csuzhucong14 分钟前
二十一 - 四十阶魔方
javascript·css·html
专业抄代码选手21 分钟前
06|双缓冲与 Commit:让 Fiber 支持第二次渲染
前端·javascript·react.js
涛涛ing21 分钟前
4.8K Star!一套规则让 AI 帮你写出“像人写的”前端代码
前端
万物智能24 分钟前
OpenHarmony源码树解剖—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
前端·后端
天才熊猫君26 分钟前
通用虚拟滚动表格行拖拽排序:从思路到完整实现
前端·javascript
hh95028 分钟前
Agent Plan × DeepSeek Harness:角色 Prompt 驱动的 Agent 分工优化与协作质量实验
java·前端·人工智能·prompt·adg·agent plan·adg成都社区
xiaominlaopodaren30 分钟前
three.js最小地图运行时(九):TileKey 线框与 cursor 坐标探针
javascript·gis·three.js
CarIise31 分钟前
JavaScript进阶与轮播图实现 课堂笔记
开发语言·javascript·笔记