在 React 中使用 JavaScript 将 Excel 转换为 SVG

在数据可视化与文档处理场景中,将 Excel 文件转换为 SVG 格式的需求日益增多。SVG 作为矢量图形格式,能够在网页端提供无损缩放、文字可检索等特性,尤其适合嵌入仪表盘、报表系统或需要打印输出的应用。

本文以 React 框架为基础,介绍如何通过前端技术实现 Excel 工作表和独立图表到 SVG 的转换,并讨论其中的关键步骤与常见问题处理。

技术选型与初始化

实现 Excel 文件解析与转换,需要借助具备 WASM 运行能力的库来处理二进制文件格式。本文采用基于 WebAssembly 的方案,该方案能够在浏览器端直接完成转换,无需后端服务支持。

首先在 React 项目中安装依赖:

bash 复制代码
npm i spire.office

安装完成后,需要在应用初始化阶段加载并启动 WASM 模块。建议在顶层组件或入口文件中完成这一步骤,确保后续操作时模块已就绪:

jsx 复制代码
// App.jsx 或 main.jsx
import React, { useEffect } from 'react';

function App() {
  useEffect(() => {
    const initWasm = async () => {
      try {
        // 加载通用模块
        await import('/node_modules/spire.office/spire.common.js');
        // 初始化 WASM 运行时
        await window.spire.initializeWasm();
        // 加载 XLS 模块
        await import('/node_modules/spire.office/spire.xls.js');
        console.log('WASM 模块初始化完成');
      } catch (error) {
        console.error('初始化失败:', error);
      }
    };
    initWasm();
  }, []);

  // 其他组件代码...
}

初始化完成后,核心功能模块会挂载到 window.spirexlswindow.xlswasm 上,后续可通过这些全局对象调用相关 API。

工作表转换:将单元格数据转为 SVG

常规工作表包含网格化的单元格数据、公式和格式信息。转换为 SVG 时,需要将整个工作表区域渲染为矢量图形。

核心步骤

转换流程包含四个关键环节:

  1. 字体预加载:将所需字体文件载入 WASM 的虚拟文件系统(VFS),确保文本渲染正确
  2. Excel 文件加载 :将目标文件读取到 VFS,通过 Workbook 对象解析
  3. 渲染输出 :获取目标工作表,调用 ToSVGStream 方法生成 SVG 数据流
  4. 导出文件:从 VFS 读取生成的 SVG 数据,构造 Blob 对象并触发下载

实现示例

jsx 复制代码
function ExcelToSvgConverter() {
  const convertSheetToSvg = async () => {
    const xlsModule = window.spirexls || window.xlswasm;
    if (!xlsModule) {
      alert('WASM 模块尚未加载完成');
      return;
    }

    try {
      // 1. 加载字体文件到 VFS
      await window.spire.FetchFileToVFS(
        'arial.ttf', 
        '/Library/Fonts/', 
        `${process.env.PUBLIC_URL}/fonts/`
      );

      // 2. 加载 Excel 文件
      const inputFile = 'SampleData.xlsx';
      await window.spire.FetchFileToVFS(
        inputFile, 
        '', 
        `${process.env.PUBLIC_URL}/data/`
      );

      // 3. 解析工作簿并获取第一个工作表
      const workbook = new xlsModule.Workbook();
      workbook.LoadFromFile({ fileName: inputFile });
      const sheet = workbook.Worksheets.get(0);

      // 4. 转换为 SVG
      const outputFile = 'worksheet_output.svg';
      const stream = new xlsModule.Stream(outputFile);
      // 参数: stream, left, top, width, height (设为0表示使用默认区域)
      sheet.ToSVGStream(stream, 0, 0, 0, 0);
      stream.Flush();
      stream.Dispose();

      // 5. 读取并下载结果
      const svgData = window.dotnetRuntime.Module.FS.readFile(outputFile);
      const blob = new Blob([svgData], { 
        type: 'image/svg+xml;charset=utf-8' 
      });
      const url = URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = url;
      link.download = outputFile;
      link.click();
      URL.revokeObjectURL(url);

      // 6. 释放资源
      workbook.Dispose();
    } catch (error) {
      console.error('转换失败:', error);
    }
  };

  return (
    <div>
      <button onClick={convertSheetToSvg}>
        将当前工作表导出为 SVG
      </button>
    </div>
  );
}

图表工作表转换:处理独立图表

Excel 中的图表工作表(ChartSheet)是一种特殊类型的工作表,其中仅包含一个图表对象,不包含单元格数据。这种场景的转换逻辑与常规工作表有所不同。

关键差异

  • 获取方式 :图表工作表需通过 GetChartSheetByName 方法按名称定位,而非通过索引
  • 渲染范围:图表本身已定义好绘制区域,转换时无需额外指定边界参数

实现示例

jsx 复制代码
function ChartSheetToSvgConverter() {
  const convertChartSheetToSvg = async () => {
    const xlsModule = window.spirexls || window.xlswasm;
    if (!xlsModule) {
      alert('WASM 模块尚未加载完成');
      return;
    }

    try {
      // 加载字体和 Excel 文件(同前)
      await window.spire.FetchFileToVFS(
        'arial.ttf', 
        '/Library/Fonts/', 
        `${process.env.PUBLIC_URL}/fonts/`
      );

      const inputFile = 'ChartData.xlsx';
      await window.spire.FetchFileToVFS(
        inputFile, 
        '', 
        `${process.env.PUBLIC_URL}/data/`
      );

      // 加载工作簿并获取图表工作表
      const workbook = new xlsModule.Workbook();
      workbook.LoadFromFile({ fileName: inputFile });
      
      // 按名称获取图表工作表
      const chartSheet = workbook.GetChartSheetByName('SalesChart');
      if (!chartSheet) {
        alert('未找到名为 SalesChart 的图表工作表');
        return;
      }

      // 转换为 SVG
      const outputFile = 'chart_output.svg';
      const stream = new xlsModule.Stream(outputFile);
      chartSheet.ToSVGStream(stream, 0, 0, 0, 0);
      stream.Flush();

      // 导出文件(同前)
      const svgData = window.dotnetRuntime.Module.FS.readFile(outputFile);
      const blob = new Blob([svgData], { 
        type: 'image/svg+xml;charset=utf-8' 
      });
      const url = URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = url;
      link.download = outputFile;
      link.click();
      URL.revokeObjectURL(url);

      workbook.Dispose();
    } catch (error) {
      console.error('图表转换失败:', error);
    }
  };

  return (
    <div>
      <button onClick={convertChartSheetToSvg}>
        将图表导出为 SVG
      </button>
    </div>
  );
}

完整组件结构

综合以上两种场景,可以构建一个包含两种转换功能的完整 React 组件:

jsx 复制代码
import React, { useState } from 'react';

function ExcelSvgConverter() {
  const [loading, setLoading] = useState(false);
  const [message, setMessage] = useState('');

  const handleConvert = async (type) => {
    setLoading(true);
    setMessage('处理中...');

    try {
      const xlsModule = window.spirexls || window.xlswasm;
      if (!xlsModule) {
        throw new Error('WASM 模块未就绪');
      }

      // 确保字体已加载
      await window.spire.FetchFileToVFS(
        'arial.ttf', 
        '/Library/Fonts/', 
        `${process.env.PUBLIC_URL}/fonts/`
      );

      // 根据类型执行不同转换逻辑
      // ... (具体转换代码略)
      
      setMessage('转换成功');
    } catch (error) {
      setMessage(`错误: ${error.message}`);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <h2>Excel 转 SVG 工具</h2>
      <div>
        <button 
          onClick={() => handleConvert('sheet')} 
          disabled={loading}
        >
          转换工作表
        </button>
        <button 
          onClick={() => handleConvert('chart')} 
          disabled={loading}
        >
          转换图表
        </button>
      </div>
      {loading && <p>加载中...</p>}
      {message && <p>{message}</p>}
    </div>
  );
}

常见问题与处理建议

文本渲染异常

如果生成的 SVG 中文字显示为空白或方框,通常是因为所需字体未正确加载到 VFS。建议在转换前显式加载文档中使用的字体文件:

javascript 复制代码
// 加载多个常用字体
const fonts = ['arial.ttf', 'simsun.ttc', 'msyh.ttf'];
for (const font of fonts) {
  await window.spire.FetchFileToVFS(
    font, 
    '/Library/Fonts/', 
    `${process.env.PUBLIC_URL}/fonts/`
  );
}

大文件处理

对于包含大量数据的工作表,转换过程可能耗时较长。建议:

  • 在转换期间显示加载状态提示
  • 考虑使用 Web Worker 将转换操作移出主线程
  • 对于超大文件,评估是否需要分页或按区域转换

文件下载失败

如果下载的 SVG 文件无法正常打开,请检查:

  1. MIME 类型是否为 image/svg+xml;charset=utf-8
  2. 浏览器是否支持 SVG 预览
  3. 控制台是否有关于文件读取的错误信息

总结

本文介绍了在 React 应用中借助 WebAssembly 技术实现 Excel 转 SVG 的两种常见场景。整体方案完全在浏览器端运行,不涉及后端服务,文件数据无需上传,在一定程度上保障了数据隐私。实现过程中需要重点关注 WASM 模块的初始化时机、字体的预加载以及转换完成后的资源释放。常规工作表与图表工作表的转换逻辑大体一致,主要区别在于前者通过索引获取,后者按名称定位。该方法适用于在线报表、数据导出等需要在前端进行文档格式转换的场景,开发者可根据实际需求调整渲染参数以适配不同的业务要求。

相关推荐
CodingSpace1 小时前
ESLint
前端
Csvn1 小时前
异步错误捕获的六大陷阱:await 裹着 try-catch 就一定稳了吗?
前端
用户059540174461 小时前
向量库静默丢数据踩坑实录:Playwright 端到端测试让我排查了72小时
前端·css
星栈2 小时前
SPA 写累了?试试 LiveView:服务端管状态,前端不写 JS
前端·前端框架·elixir
labixiong2 小时前
手写Promise--微任务、静态方法、async/await 全搞懂(三)
前端·javascript
Asize2 小时前
CSS 3D:从布局到立方体
前端
梨子同志2 小时前
React
前端
万少2 小时前
22 点后,我靠这个 AI 工具成了"夜间天才程序员"
前端·后端
狂师2 小时前
比 Playwright 更给力,推荐一个AI Agent的浏览器自动化开源项目!
前端·开源·测试