在数据可视化应用中,将 Excel 工作簿中的图表和形状导出为独立图像,是提升数据内容跨平台兼容性的常见需求。将图表和形状转换为 PNG、JPG 等通用格式后,可以确保可视化内容在网页、移动端或非 Office 环境中正确展示,同时也便于嵌入报告或演示文稿。
本文介绍一种在 React 应用中实现 Excel 图表和形状图片导出的前端方案。该方案基于 WebAssembly 技术,能够在浏览器端直接处理 Excel 文件,无需后端服务支持。
安装与配置
首先在 React 项目中安装依赖:
bash
npm i spire.office
安装完成后,需要将 spire.xls.js 和 spire.xls.wasm 文件放置在项目的 public 目录下。这两个文件是 WebAssembly 模块的核心组成部分,负责在浏览器端解析和处理 Excel 文件。
在 React 组件中通过动态导入加载 WebAssembly 模块:
jsx
import React, { useState, useEffect } from 'react';
import JSZip from 'jszip';
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('加载 spire.xls.js WASM 模块失败:', error);
}
})();
}, []);
}
将 Excel 图表保存为图片
使用 Workbook.SaveChartAsImage() 方法可以将工作表中的特定图表保存为图片文件。这些图片会存储在 WebAssembly 的虚拟文件系统(VFS)中,之后可以打包为 ZIP 下载。
核心步骤如下:
- 使用
FetchFileToVFS()将字体文件和 Excel 文件加载到 VFS - 创建
Workbook实例并加载 Excel 文件 - 遍历工作表和图表,使用
SaveChartAsImage()保存为图片 - 从 VFS 读取图片文件,打包为 ZIP 并触发下载
jsx
const SaveExcelChartAsImage = async () => {
const wasmModule = window.wasmModule.spirexls;
if (wasmModule) {
// 将字体加载到虚拟文件系统(VFS)中
await window.spire.FetchFileToVFS('Arial.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
// 将 Excel 文件加载到虚拟文件系统
const inputFileName = '示例.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);
// 在 VFS 中创建图像文件夹
const imageFolderName = `Images`;
window.dotnetRuntime.Module.FS.mkdirTree(imageFolderName);
// 创建一个新的工作簿
const workbook = new wasmModule.Workbook();
// 从虚拟文件系统加载 Excel 文件
workbook.LoadFromFile(inputFileName);
// 遍历工作簿中的每个工作表
for (let i = 0; i < workbook.Worksheets.Count; i++) {
const sheet = workbook.Worksheets.get(i);
for (let j = 0; j < sheet.Charts.Count; j++) {
let image = workbook.SaveChartAsImage({ worksheet: sheet, chartIndex: j });
let filePath = `${imageFolderName}/${sheet.Name}_Chart-${j}.png`;
image.Save(filePath);
}
}
// 将图像文件夹打包为 ZIP 并下载
// ...
}
};
将 Excel 形状保存为图片
处理形状的流程与图表类似,区别在于使用 Worksheet.PrstGeomShapes.get() 获取形状对象,然后调用 shape.SaveToImage() 方法。
jsx
const SaveExcelShapeAsImage = async () => {
const wasmModule = window.wasmModule.spirexls;
if (wasmModule) {
// 将字体加载到虚拟文件系统(VFS)中
await window.spire.FetchFileToVFS('Arial.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
// 将 Excel 文件加载到虚拟文件系统
const inputFileName = 'Shape.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);
// 在 VFS 中创建图像文件夹
const imageFolderName = `Images`;
window.dotnetRuntime.Module.FS.mkdirTree(imageFolderName);
// 创建一个新的工作簿
const workbook = new wasmModule.Workbook();
// 从虚拟文件系统加载 Excel 文件
workbook.LoadFromFile(inputFileName);
// 遍历工作簿中的每个工作表
for (let i = 0; i < workbook.Worksheets.Count; i++) {
const sheet = workbook.Worksheets.get(i);
for (let j = 0; j < sheet.PrstGeomShapes.Count; j++) {
const shape = sheet.PrstGeomShapes.get(j);
const image = shape.SaveToImage();
let filePath = `${imageFolderName}/${sheet.Name}_shape-${j}.png`;
image.Save(filePath);
}
}
// 将图像文件夹打包为 ZIP 并下载
// ...
}
};
打包 ZIP 并触发下载
从 VFS 中读取文件并打包为 ZIP 下载的通用逻辑如下:
jsx
// 递归函数,将目录及其内容添加到 ZIP 中
const addFilesToZip = (folderPath, zipFolder) => {
const items = window.dotnetRuntime.Module.FS.readdir(folderPath);
items.filter(item => item !== "." && item !== "..").forEach((item) => {
const itemPath = `${folderPath}/${item}`;
try {
const fileData = window.dotnetRuntime.Module.FS.readFile(itemPath);
zipFolder.file(item, fileData);
} catch (error) {
if (error.code === 'EISDIR') {
const zipSubFolder = zipFolder.folder(item);
addFilesToZip(itemPath, zipSubFolder);
} else {
console.error(`处理 ${itemPath} 时出错:`, error);
}
}
});
};
// 将图像文件夹打包成 ZIP 文件
const zip = new JSZip();
addFilesToZip(imageFolderName, zip);
// 从 ZIP 文件生成 Blob 并触发下载
zip.generateAsync({ type: "blob" })
.then(function(content) {
const link = document.createElement('a');
link.href = URL.createObjectURL(content);
link.download = 'images.zip';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
}).catch(function(err) {
console.error("生成 ZIP 文件时出错:", err);
});
虚拟文件系统与资源管理
虚拟文件系统(VFS) 是这套方案工作机制中的关键环节。通过 Save() 方法生成的文件会存储在 WebAssembly 的内存文件系统中,而非用户的物理磁盘。导出时需要从 VFS 中读取文件数据,再转换为浏览器可识别的 Blob 对象,最后触发下载。
FS.mkdirTree() 方法用于递归创建目录,与 FS.mkdir() 相比,它能一次性创建多级目录结构,在处理嵌套路径时更加便捷。
每个 Workbook 实例在完成操作后,建议显式调用 Dispose() 方法释放内存资源,尤其是在频繁导出或处理大文件的场景中,及时释放资源有助于避免浏览器内存占用过高。
输出格式选择
该方案支持将图表和形状输出为 PNG、JPG、BMP 等常见图片格式。如果需要矢量图形输出,也可以将图表或工作表转换为 SVG 格式。SVG 格式在网页嵌入场景中具有可任意缩放、文字可选等优势,而 PNG 格式则在通用兼容性方面更有优势,开发者可以根据实际应用场景选择适合的输出格式。
小结
通过在 React 中集成基于 WebAssembly 的 Excel 处理模块,可以完全在浏览器端完成 Excel 图表和形状的图片导出工作,无需依赖后端服务。该方案通过虚拟文件系统管理输入输出文件,适用于需要在前端直接处理 Excel 可视化内容的场景。在实际项目中,开发者可根据业务需求对上述示例代码进行适配和扩展。