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 格式,并注意处理好文档中的字体资源。

相关推荐
AlienZHOU4 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
Captaincc7 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师9 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen9 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒9 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow10 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林81810 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang8007810 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明10 小时前
第 6 章 组件进阶
前端·vue.js
江华森10 小时前
HTTP请求的完整过程详解:从DNS解析到TCP挥手的微秒级实战分析
前端