在 B 端业务开发中,前端经常需要处理 Excel 文件的生成与导出。除了基础的数据写入,某些场景下我们还需要对文件施加安全控制,比如对整个工作簿加密、限制工作表的编辑权限,或者解除已有的保护。本文将结合 Spire.XLS for JavaScript,详细介绍在 React 项目中如何通过 JavaScript 实现这些操作。
环境搭建
首先通过 npm 安装依赖包:
npm i spire.office
安装完成后,在 React 组件中引入 WASM 模块。由于该库基于 WebAssembly 实现,使用时需要先异步加载引擎。通常的做法是在 useEffect 中完成初始化,并将加载状态通过 useState 管理,以控制后续操作的可用性:
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('WASM 模块加载失败:', error);
}
})();
}, []);
// 后续操作方法将在此处定义...
}
这里使用动态 import 加载 spire.xls.js,并指定 WASM 文件的路径。加载成功后将模块实例存入 window.wasmModule,方便后续调用。wasmModule 状态则用于控制按钮的可点击状态,避免在引擎未就绪时触发操作。
操作前的准备工作
在实际处理 Excel 文件前,有两项资源需要预先加载到库的虚拟文件系统(VFS)中:字体文件和待处理的 Excel 文件。字体文件用于保证文档中的文字能正常渲染,Excel 文件则是我们要操作的数据源。
// 加载字体到 VFS
await window.spire.FetchFileToVFS('Arial.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
// 加载 Excel 文件到 VFS
const inputFileName = 'sample.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);
FetchFileToVFS 的三个参数依次为:文件名、VFS 中的目标目录、浏览器端的源文件路径。以上代码假设字体文件存放在 public/static/font/ 目录下,Excel 文件存放在 public/static/data/ 目录下。开发者可根据项目实际结构调整路径。
使用密码保护整个工作簿
工作簿级别的保护会要求用户在打开文件时输入密码,适用于需要控制文件访问权限的场景。下面是一个完整的处理函数:
const EncryptExcel = async () => {
const wasmModule = window.wasmModule.spirexls;
if (wasmModule) {
// 加载字体和源文件到 VFS(此处省略,实际开发需保留)
// ...
// 创建工作簿实例并加载文件
const workbook = new wasmModule.Workbook();
workbook.LoadFromFile(inputFileName);
// 使用密码加密工作簿
workbook.Protect('password');
// 定义输出文件名并保存
const outputFileName = '加密Excel文件.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: wasmModule.ExcelVersion.Version2010 });
// 从 VFS 读取生成的文件,转换为 Blob 并触发下载
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const modifiedFile = new Blob([modifiedFileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
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);
// 释放 WASM 资源
workbook.Dispose();
}
};
整体流程可概括为五步:加载文件到 VFS → 创建 Workbook 实例 → 调用 Protect 设置密码 → 保存文件 → 从 VFS 读取并下载。其中 SaveToFile 的 version 参数指定了输出文件的 Excel 版本格式,这里使用 Version2010 即 .xlsx 格式。操作完成后调用 Dispose 是一个好习惯,可以及时释放 WebAssembly 占用的内存。
设置特定权限保护工作表
有时我们并不需要加密整个文件,而是希望限制用户对某个工作表的操作。比如分发数据报表时,允许查看但不允许修改。此时可以通过设置工作表保护类型来实现细粒度控制:
const EncryptExcelWorksheet = async () => {
const wasmModule = window.wasmModule.spirexls;
if (wasmModule) {
// 加载字体和源文件到 VFS(此处省略)
// ...
const workbook = new wasmModule.Workbook();
workbook.LoadFromFile(inputFileName);
// 获取第一个工作表
const sheet = workbook.Worksheets.get(0);
// 使用特定权限保护工作表
sheet.Protect({ password: '123456', options: wasmModule.SheetProtectionType.None });
// 保存并下载(与上文下载逻辑相同,此处省略)
// ...
workbook.Dispose();
}
};
这里的关键参数是 SheetProtectionType,示例中使用了 None 表示禁止所有用户操作。库还提供了其他保护类型,开发者可以根据需求组合使用,例如仅禁止格式化而允许数据选择等。这种精细化的权限设置在制作标准化数据采集模板时非常实用。
取消工作表的保护
当文件需要重新编辑时,可以通过正确的密码解除工作表保护:
const UnprotectExcelWorksheet = async () => {
const wasmModule = window.wasmModule.spirexls;
if (wasmModule) {
// 加载受保护的 Excel 文件到 VFS
const inputFileName = '加密Excel工作表.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);
const workbook = new wasmModule.Workbook();
workbook.LoadFromFile(inputFileName);
// 获取需要取消保护的工作表
const sheet = workbook.Worksheets.get(0);
// 使用正确密码移除保护
sheet.Unprotect('password');
// 保存并下载(此处省略)
// ...
workbook.Dispose();
}
};
解除保护后保存的新文件将不再受工作表编辑限制。值得注意的是,如果密码输入错误,Unprotect 方法会抛出异常。在生产环境中,建议使用 try-catch 捕获异常并给用户相应的提示信息。
组件整合示例
将上述功能整合到一个 React 组件中,完整的渲染结构如下:
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>React 中 JavaScript 保护与取消保护 Excel 工作簿</h1>
<button onClick={EncryptExcel} disabled={!wasmModule}>
加密工作簿并下载
</button>
<button onClick={EncryptExcelWorksheet} disabled={!wasmModule}>
保护工作表并下载
</button>
<button onClick={UnprotectExcelWorksheet} disabled={!wasmModule}>
解除工作表保护并下载
</button>
</div>
);
通过 disabled={!wasmModule} 确保在 WASM 引擎加载完成前按钮不可点击,避免因过早调用导致运行时错误。
技术要点小结
回顾整个实现过程,有几个关键点值得注意:
第一,WASM 模块的初始化是异步过程,务必在加载完成后再进行文件操作,否则会因引擎未就绪而报错。
第二,库使用独立的虚拟文件系统管理文件,输入文件需要先通过 FetchFileToVFS 导入,输出文件则需要通过 FS.readFile 读取后再转换格式。这与直接操作浏览器文件系统的思路有所不同。
第三,密码在示例中以明文形式写在代码中。如果项目对安全性有较高要求,建议改为由用户在页面上输入密码,避免将敏感信息直接暴露在前端代码中。
第四,每次操作完成后调用 Dispose 有助于释放 WebAssembly 内存,尤其是在连续处理多个文件的场景下更应关注资源管理。
结语
通过上述示例可以看到,在 React 前端项目中实现对 Excel 工作簿和工作表的保护与取消保护是可行的。整个过程不依赖后端服务,所有文件处理均在浏览器端完成,这种方案在需要纯前端处理文档的场景中具有一定的实用价值。无论是生成受控的数据报表,还是为用户提供文件加密工具,本文介绍的技术思路都可以作为参考基础进行扩展。希望这些内容对正在探索前端文档处理方向的开发者有所帮助。