在 React 中使用 JavaScript 合并 Excel 文件

在企业级前端应用中,Excel 文件的导入、导出和处理是频繁出现的需求。其中,文件合并操作尤为典型------可能是将多个报表汇总为一个,也可能是将同一文件中的多个工作表整合为一张表格。本文基于 React 框架,介绍通过 WebAssembly 技术在浏览器端完成合并操作的具体实现方式。


前置环境配置

该方案依赖于 WebAssembly 模块,需要在 React 项目中完成安装和初始化加载。

安装依赖:

bash 复制代码
npm i spire.office

安装完成后,将 node_modules/spire.office 目录中的 spire.xls.jsspire.xls.wasm 两个文件复制到项目的 public 目录下。

在 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.xls.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 spire.xls.js WASM module:', error);
      }
    })();
  }, []);

  // 后续操作...
}

模块加载完成后,通过 window.wasmModule.spirexls 即可调用所有 Excel 操作接口。


场景一:将多个 Excel 工作簿合并为一个文件

该场景适用于:将不同部门、不同时间段或不同来源的多个 .xlsx 文件汇总成一份完整的工作簿,每个源文件的内容作为一个独立的 Sheet 页保留。

实现思路

  1. 创建一个新的空工作簿,作为合并目标;
  2. 逐个加载源文件到临时工作簿对象;
  3. 遍历临时工作簿中的每个工作表,通过 AddCopy 方法将其复制到目标工作簿;
  4. 将合并结果保存并导出为 Blob 文件。

关键代码

jsx 复制代码
const MergeExcelWorkbooks = async () => {
  const wasmModule = window.wasmModule.spirexls;

  if (wasmModule) {
    // 将字体加载到虚拟文件系统(VFS)中
    await window.spire.FetchFileToVFS('Arial.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);

    // 将 Excel 文件加载到虚拟文件系统 (VFS)
    const files = [
      "文档1.xlsx",
      "文档2.xlsx",
      "文档3.xlsx",
    ];
    for (const file of files) {
      window.spire.FetchFileToVFS(file, '', `${process.env.PUBLIC_URL}/static/data/`);
    }

    // 创建一个新的工作簿
    const newbook = new wasmModule.Workbook();
    newbook.Version = wasmModule.ExcelVersion.Version2013;

    // 清除默认工作表
    newbook.Worksheets.Clear();

    // 创建一个临时工作簿
    let tempbook = new wasmModule.Workbook();

    for (const file of files) {
      // 加载当前文件
      tempbook.LoadFromFile(file.split("/").pop());

      for (let i = 0; i < tempbook.Worksheets.Count; i++) {
        let sheet = tempbook.Worksheets.get(i);
        // 将当前文件中的每个工作表复制到新工作簿
        wasmModule.XlsWorksheetsCollection.Convert(
          newbook.Worksheets
        ).AddCopy({
          sheet: sheet,
          flags: wasmModule.WorksheetCopyType.CopyAll,
        });
      }
    }

    let outputFileName = "合并工作簿.xlsx";
    newbook.SaveToFile({ fileName: outputFileName, version: wasmModule.ExcelVersion.Version2013 });

    // 读取保存的文件并转换为Blob对象
    const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const modifiedFile = new Blob([modifiedFileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });

    // 为Blob创建一个URL并启动下载
    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);

    // 清理工作簿使用的资源
    newbook.Dispose();
  }
};

场景二:将同一工作簿中的多个 Sheet 合并为一个 Sheet

另一种常见需求是:一个 Excel 文件中包含多个 Sheet 页,但最终需要将它们的全部数据整合到一张表内,例如将 1 月、2 月、3 月的分页数据纵向拼接。

实现思路

  1. 加载包含多个 Sheet 的目标文件;
  2. 获取第一个工作表(作为目标表)和第二个工作表;
  3. 获取第二个工作表的已使用数据范围(AllocatedRange);
  4. 将该数据范围复制到第一个工作表已有数据的末尾行;
  5. 删除已被复制的工作表,保留合并后的单张 Sheet。

关键代码

jsx 复制代码
const MergeWorksheets = async () => {
  const wasmModule = window.wasmModule.spirexls;

  if (wasmModule) {
    // 将字体加载到虚拟文件系统(VFS)中
    await window.spire.FetchFileToVFS('Arial.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);

    // 将 Excel 文件加载到虚拟文件系统 (VFS)
    let inputFileName = '示例.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);

    // 创建一个新的工作簿
    const workbook = new wasmModule.Workbook();

    // 从虚拟文件系统加载 Excel 文件
    workbook.LoadFromFile(inputFileName);

    // 获取第一个工作表
    let sheet1 = workbook.Worksheets.get(0);

    // 获取第二个工作表
    let sheet2 = workbook.Worksheets.get(1);

    // 获取第二个工作表中所使用的数据区域
    let fromRange = sheet2.AllocatedRange;

    // 指定第一个工作表中的目标区域
    let toRange = sheet1.Range.get({ row: sheet1.LastRow + 1, column: 1 });

    // 将第二个工作表的数据区域复制到第一个工作表的目标区域
    fromRange.Copy({ destRange: toRange });

    // 删除第二个工作表
    sheet2.Remove();

    // 定义输出文件名
    const outputFileName = "合并工作表.xlsx";
    workbook.SaveToFile({ fileName: outputFileName, version: wasmModule.ExcelVersion.Version2010 });

    // 读取保存的文件并转换为Blob对象
    const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const modifiedFile = new Blob([modifiedFileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });

    // 为Blob创建一个URL并启动下载
    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);

    // 清理工作簿使用的资源
    workbook.Dispose();
  }
};

关于虚拟文件系统与资源释放

上述所有文件读写操作均发生在 WebAssembly 的虚拟文件系统(VFS)中,文件不会实际存储在浏览器本地或上传至任何服务器。数据流为:

  1. FetchFileToVFS 将外部文件读入 VFS;
  2. 操作后通过 SaveToFile 将结果写回 VFS;
  3. 最后使用 FS.readFile 读取为二进制数据并导出为 Blob。

每次操作完成后,建议显式调用 Dispose() 方法释放工作簿对象所占用的 WASM 内存,避免长时间运行后出现内存累积。


总结

本文介绍了在 React 中通过 WebAssembly 方案合并 Excel 文件的两种场景:多工作簿合并与多工作表合并。该方案基于虚拟文件系统(VFS)在浏览器内存中完成所有操作,数据无需上传服务器,处理流程为:加载到 VFS → 执行合并 → 保存结果 → 导出为 Blob 下载。每次操作完成后需调用 Dispose() 释放 WASM 内存,两种场景的选择取决于业务需求------多工作簿合并保留各源文件的 Sheet 结构,多工作表合并则将多个 Sheet 纵向拼接为一张表。实际开发中需注意文件路径配置、中文字体加载及大文件性能问题,该方案为纯前端 Excel 处理提供了一条可行路径。

相关推荐
大流星1 小时前
LangChainJs之基础模型(一)
javascript·langchain
橘子星1 小时前
JavaScript this 指向全解实战指南
前端·javascript
何出无名之师1 小时前
AIDL的一次调用链路追踪之二,如何和驱动打交道
前端
weedsfly1 小时前
JS垃圾回收:从原理到项目实战,彻底根治内存泄漏
前端·javascript·面试
Jcc1 小时前
虚拟 DOM 是什么?从 Snabbdom 理解 Vue 的 DOM 更新机制
前端
user62229864925811 小时前
Vue 常用技术知识全景:从响应式到组件通信的系统理解
前端
feiyu_gao1 小时前
一个人 + AI:246 commits 做出设计系统 CLI 的故事
前端·ai编程·交互设计
奶油mm1 小时前
从 0 到 1 搭建高可用 Redis Cluster:踩坑、优化与生产实践
前端