在企业级前端应用中,Excel 文件的导入、导出和处理是频繁出现的需求。其中,文件合并操作尤为典型------可能是将多个报表汇总为一个,也可能是将同一文件中的多个工作表整合为一张表格。本文基于 React 框架,介绍通过 WebAssembly 技术在浏览器端完成合并操作的具体实现方式。
前置环境配置
该方案依赖于 WebAssembly 模块,需要在 React 项目中完成安装和初始化加载。
安装依赖:
bash
npm i spire.office
安装完成后,将 node_modules/spire.office 目录中的 spire.xls.js 和 spire.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 页保留。
实现思路
- 创建一个新的空工作簿,作为合并目标;
- 逐个加载源文件到临时工作簿对象;
- 遍历临时工作簿中的每个工作表,通过
AddCopy方法将其复制到目标工作簿; - 将合并结果保存并导出为 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 月的分页数据纵向拼接。
实现思路
- 加载包含多个 Sheet 的目标文件;
- 获取第一个工作表(作为目标表)和第二个工作表;
- 获取第二个工作表的已使用数据范围(
AllocatedRange); - 将该数据范围复制到第一个工作表已有数据的末尾行;
- 删除已被复制的工作表,保留合并后的单张 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)中,文件不会实际存储在浏览器本地或上传至任何服务器。数据流为:
FetchFileToVFS将外部文件读入 VFS;- 操作后通过
SaveToFile将结果写回 VFS; - 最后使用
FS.readFile读取为二进制数据并导出为 Blob。
每次操作完成后,建议显式调用 Dispose() 方法释放工作簿对象所占用的 WASM 内存,避免长时间运行后出现内存累积。
总结
本文介绍了在 React 中通过 WebAssembly 方案合并 Excel 文件的两种场景:多工作簿合并与多工作表合并。该方案基于虚拟文件系统(VFS)在浏览器内存中完成所有操作,数据无需上传服务器,处理流程为:加载到 VFS → 执行合并 → 保存结果 → 导出为 Blob 下载。每次操作完成后需调用 Dispose() 释放 WASM 内存,两种场景的选择取决于业务需求------多工作簿合并保留各源文件的 Sheet 结构,多工作表合并则将多个 Sheet 纵向拼接为一张表。实际开发中需注意文件路径配置、中文字体加载及大文件性能问题,该方案为纯前端 Excel 处理提供了一条可行路径。