拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Node.js Excel文件处理实战:从xlsx库读取到生成完整指南

1. 从零开始为什么Node.js需要处理Excel文件如果你做过Web后端开发或者写过一些需要处理数据的脚本大概率会遇到一个场景用户上传了一个Excel文件你需要解析里面的数据或者你需要把数据库里的一堆数据整理成一个格式规整的Excel报表然后让用户下载。在Node.js生态里处理Excel文件最常用、功能最全面的库之一就是xlsx。它几乎成了这个领域的“事实标准”就像处理日期会用moment或dayjs一样。这个库的强大之处在于它完全用JavaScript实现不依赖任何本地Excel程序比如微软的Office或者金山的WPS这意味着你可以在任何能运行Node.js的环境比如Linux服务器、Docker容器里使用它。无论是读取.xlsx、.xls还是.csv格式还是生成包含复杂格式、公式、甚至多个工作表Sheet的Excel文件它都能搞定。我最初接触它是因为一个数据迁移项目需要把旧系统里导出的几十个Excel文件清洗、合并再导入新数据库。手动操作是不可能的用Python的pandas虽然也行但整个技术栈是Node.js为了一个功能引入另一种语言环境太折腾。xlsx库完美地解决了这个问题。接下来的内容我会以一个完整的、可运行的例子为主线带你走一遍从安装、读取、处理到生成Excel文件的完整流程。我会重点讲清楚每个步骤背后的“为什么”以及我在实际项目中踩过的那些坑。你会发现用Node.js操作Excel远比你想象的要简单和强大。2. 环境准备与xlsx库的安装在开始写代码之前我们得先把“战场”布置好。这里假设你已经安装了Node.js和npmNode包管理器。如果你还没装可以去Node.js官网下载LTS长期支持版本安装过程基本是“下一步”到底。注意安装Node.js时如果遇到系统提示禁止运行脚本的错误比如热搜词里提到的“npm : 无法加载文件...因为在此系统上禁止运行脚本”这是因为Windows系统的执行策略限制。解决方法是以管理员身份打开PowerShell运行Set-ExecutionPolicy RemoteSigned然后选Y确认。这是个一次性操作。创建一个新的项目目录并初始化它mkdir node-excel-demo cd node-excel-demo npm init -y这行命令会生成一个package.json文件记录项目的依赖。接下来安装核心的xlsx库npm install xlsx安装完成后你的package.json的dependencies里就会看到xlsx。这里有个小细节xlsx库本身功能很全但如果你需要处理非常大的文件比如几百MB可能会遇到内存问题。社区里也有像exceljs这样的库它在处理大文件和流式写入方面有优势。但对于绝大多数日常场景——文件大小在几十MB以内——xlsx的稳定性和功能丰富度是首选。我选择它就是因为其API稳定社区活跃遇到问题基本都能搜到解决方案。为了测试我们还需要一个Excel文件。你可以用WPS或Microsoft Excel自己创建一个简单的test.xlsx包含一个“用户信息”表内容如下姓名年龄城市入职日期张三28北京2023-01-15李四35上海2020-08-22王五24广州2024-03-10把它保存在项目根目录下。我们的第一个任务就是读取它。3. 核心读取把Excel文件变成JavaScript对象读取是操作的第一步目标是把磁盘上那个二进制的.xlsx文件转换成我们在内存里可以随意操作的JavaScript数据结构。xlsx库提供了同步和异步两种读取方式为了代码清晰我们先从同步读法开始。创建一个名为readExcel.js的文件const XLSX require(xlsx); const path require(path); // 1. 解析文件路径 const filePath path.join(__dirname, test.xlsx); // 2. 同步读取文件得到工作簿Workbook对象 const workbook XLSX.readFile(filePath); // 3. 查看工作簿里包含的所有工作表Sheet名称 console.log(工作表名称列表, workbook.SheetNames); // 4. 根据名称获取第一个工作表 const firstSheetName workbook.SheetNames[0]; const worksheet workbook.Sheets[firstSheetName]; // 5. 将工作表转换成JSON数据 // header: 1 选项表示将第一行作为标题行并生成二维数组 // header: ‘A’ 会生成键为单元格地址的对象。我们通常用header: 1 const jsonData XLSX.utils.sheet_to_json(worksheet, { header: 1 }); console.log(原始转换数据二维数组); console.log(jsonData); // 6. 更常用的方式将第一行作为对象的键 const dataWithHeaders XLSX.utils.sheet_to_json(worksheet); console.log(\n结构化数据对象数组); console.log(dataWithHeaders);运行这个脚本node readExcel.js。你会看到类似下面的输出工作表名称列表 [ ‘Sheet1’ ] 原始转换数据二维数组 [ [ ‘姓名’ ‘年龄’ ‘城市’ ‘入职日期’ ] [ ‘张三’ 28 ‘北京’ ‘2023-01-15’ ] [ ‘李四’ 35 ‘上海’ ‘2020-08-22’ ] [ ‘王五’ 24 ‘广州’ ‘2024-03-10’ ] ] 结构化数据对象数组 [ { ‘姓名’ ‘张三’ ‘年龄’ 28 ‘城市’ ‘北京’ ‘入职日期’ ‘2023-01-15’ } { ‘姓名’ ‘李四’ ‘年龄’ 35 ‘城市’ ‘上海’ ‘入职日期’ ‘2020-08-22’ } { ‘姓名’ ‘王五’ ‘年龄’ 24 ‘城市’ ‘广州’ ‘入职日期’ ‘2024-03-10’ } ]核心原理与踩坑点workbook对象这是XLSX.readFile返回的核心对象。它不仅仅包含数据还包含所有工作表的定义、格式、公式等信息。workbook.SheetNames是一个数组按顺序存放所有Sheet的名字。workbook.Sheets是一个对象以Sheet名为键对应的worksheet对象为值。worksheet对象这是一个非常“原始”的数据结构它本质上是一个以单元格地址如A1B2为键以单元格对象为值的映射。单元格对象包含v原始值、t类型如s字符串、n数字、w格式化文本等属性。直接操作它很繁琐所以我们需要XLSX.utils.sheet_to_json来转换。sheet_to_json的header选项这是第一个容易踩坑的地方。header: 1这是我们例子中的第一种用法。它告诉库把工作表数据当成一个二维数组返回第一行就是数组的第一个子数组。这种格式适合机器处理但失去了列名的语义。header: ‘A’返回一个对象数组但每个对象的键是Excel的列字母如{ A: ‘张三’ B: 28 }很不直观基本不用。不传或header: ‘A’的另一种形式这是我们例子中的第二种用法。库会默认将工作表的第一行作为JSON对象的键属性名。这是最常用、最直观的方式。但这里有个大坑如果Excel第一行的某个单元格是空的那么对应的属性名可能就是空字符串或者undefined会导致后续数据处理出错。所以确保你的Excel表头是完整、无合并单元格的。数据类型识别xlsx会尽力识别单元格的数据类型。数字、日期会转换成JavaScript的Number和Date对象但sheet_to_json默认输出时日期会变成类似2023-01-15的ISO字符串。布尔值会变成true/false。如果遇到它无法识别的格式或者单元格里是公式v值可能是原始公式字符串如A1B1你需要通过cell.f来判断。异步读取如果你的应用是Web服务器在处理用户上传的文件时应该使用异步API避免阻塞事件循环。xlsx库本身没有提供异步的readFile但我们可以结合Node.js的fs.promises来实现const XLSX require(xlsx); const fs require(fs).promises; async function readExcelAsync(filePath) { try { // 异步读取文件Buffer const fileBuffer await fs.readFile(filePath); // 用Buffer进行解析 const workbook XLSX.read(fileBuffer, { type: ‘buffer’ }); const worksheet workbook.Sheets[workbook.SheetNames[0]]; return XLSX.utils.sheet_to_json(worksheet); } catch (error) { console.error(‘读取Excel文件失败’ error); throw error; } }关键点在于XLSX.read的第二个参数{ type: ‘buffer’ }它告诉库我们提供的是一个二进制Buffer。4. 数据处理清洗、转换与计算读取到JSON数据后我们通常不会直接使用而是要进行一系列清洗和转换。这是业务逻辑的核心部分。假设我们从dataWithHeaders这个对象数组开始。场景一数据清洗我们的数据里“年龄”应该是数字“入职日期”应该是日期对象。但读取进来可能都是字符串。const cleanedData dataWithHeaders.map(row { // 创建一个新对象避免修改原数据 const newRow { …row }; // 清洗年龄确保是数字 newRow.年龄 Number(newRow.年龄); if (isNaN(newRow.年龄)) { newRow.年龄 null; // 或者提供一个默认值 console.warn(“${newRow.姓名}”的年龄“${row.年龄}”转换失败); } // 清洗日期将字符串转为Date对象 // 注意Excel的日期在内部可能是一个数字从1899-12-30开始的天数 // 但通过sheet_to_json转换后如果单元格格式是日期通常会得到ISO字符串。 // 这里我们假设读取到的是‘YYYY-MM-DD’字符串。 if (newRow.入职日期 typeof newRow.入职日期 ‘string’) { const dateObj new Date(newRow.入职日期); if (isNaN(dateObj.getTime())) { // 检查日期是否无效 console.warn(“${newRow.姓名}”的入职日期“${newRow.入职日期}”格式错误); newRow.入职日期 null; } else { newRow.入职日期 dateObj; } } // 可以添加更多清洗逻辑比如城市名称标准化 const cityMap { ‘北京’: ‘Beijing’ ‘上海’: ‘Shanghai’ ‘广州’: ‘Guangzhou’ }; if (cityMap[newRow.城市]) { newRow.城市 cityMap[newRow.城市]; } return newRow; }); console.log(‘\n清洗后的数据’); console.log(cleanedData);场景二数据计算与衍生字段基于现有数据计算新的指标。例如计算工龄假设当前日期是2024-05-27。const currentDate new Date(‘2024-05-27’); const dataWithSeniority cleanedData.map(row { if (row.入职日期 instanceof Date) { // 计算相差的毫秒数转换为年粗略计算 const diffTime Math.abs(currentDate - row.入职日期); const diffYears diffTime / (1000 * 60 * 60 * 24 * 365.25); row.工龄 Math.floor(diffYears); // 取整 } else { row.工龄 null; } return row; }); console.log(‘\n添加工龄后的数据’); console.log(dataWithSeniority);场景三数据筛选与聚合找出年龄大于30岁的员工或者按城市分组统计平均年龄。// 筛选 const olderThan30 dataWithSeniority.filter(row row.年龄 30); console.log(‘\n年龄大于30的员工’ olderThan30); // 聚合 const cityStats {}; dataWithSeniority.forEach(row { if (!cityStats[row.城市]) { cityStats[row.城市] { count: 0 totalAge: 0 }; } cityStats[row.城市].count 1; cityStats[row.城市].totalAge row.年龄; }); for (const city in cityStats) { cityStats[city].averageAge (cityStats[city].totalAge / cityStats[city].count).toFixed(2); } console.log(‘\n按城市统计的平均年龄’ cityStats);这些操作都是标准的JavaScript数组和对象处理xlsx库的任务只是把数据从Excel里“搬”出来剩下的就交给你的业务逻辑了。5. 核心生成从JSON数据到Excel文件数据处理完了接下来就是“写回去”或者生成一个新的Excel文件。这是xlsx库另一个强大的功能。我们想把dataWithSeniority这个包含了工龄的数据写成一个新的Excel文件并且希望它好看一点比如把表头加粗给“工龄”列加上颜色。创建一个writeExcel.js文件const XLSX require(‘xlsx’); const path require(‘path’); // 假设这是我们处理好的数据 const dataToWrite [ { ‘姓名’: ‘张三’ ‘年龄’: 28 ‘城市’: ‘Beijing’ ‘入职日期’: new Date(‘2023-01-15’) ‘工龄’: 1 } { ‘姓名’: ‘李四’ ‘年龄’: 35 ‘城市’: ‘Shanghai’ ‘入职日期’: new Date(‘2020-08-22’) ‘工龄’: 4 } { ‘姓名’: ‘王五’ ‘年龄’: 24 ‘城市’: ‘Guangzhou’ ‘入职日期’: new Date(‘2024-03-10’) ‘工龄’: 0 } ]; // 1. 创建一个新的工作簿 const workbook XLSX.utils.book_new(); // 2. 将JSON数据转换为工作表对象 // 注意Date对象会被转换但格式是原始的。 const worksheet XLSX.utils.json_to_sheet(dataToWrite); // 3. 可选但重要定义单元格样式 // xlsx库的样式设置比较底层需要通过!cols !rows !merges等属性操作。 // 我们先获取工作表的范围知道它有多大。 const range XLSX.utils.decode_range(worksheet[‘!ref’]); // ‘!ref’ 定义了工作表的数据范围如 ‘A1:E4’ // 3.1 设置列宽 worksheet[‘!cols’] [ { wch: 10 } // 第1列A列姓名宽度为10字符 { wch: 6 } // 第2列B列年龄 { wch: 12 } // 第3列C列城市 { wch: 12 } // 第4列D列入职日期 { wch: 8 } // 第5列E列工龄 ]; // 3.2 设置第一行表头的样式加粗、居中 for (let C range.s.c; C range.e.c; C) { // 遍历所有列 const cellAddress XLSX.utils.encode_cell({ r: range.s.r c: C }); // 第一行r0的每个单元格地址 if (!worksheet[cellAddress]) continue; // 初始化单元格的样式对象 worksheet[cellAddress].s { font: { bold: true } alignment: { horizontal: ‘center’ } }; } // 3.3 为“工龄”列E列设置条件格式示例工龄3的标为浅绿色 // 这里演示的是直接设置单元格填充色真正的条件格式更复杂需要用到xlsx的扩展功能。 // 简单实现遍历“工龄”列手动判断并设置样式。 const工龄列索引 4; // E列是第5列索引是4从0开始 for (let R range.s.r 1; R range.e.r; R) { // 从第二行开始跳过表头 const cellAddress XLSX.utils.encode_cell({ r: R c: 工龄列索引 }); const cell worksheet[cellAddress]; if (cell cell.v 3) { cell.s { …(cell.s || {}) fill: { fgColor: { rgb: “FFC6EFCE” } } }; // 浅绿色 } } // 4. 将工作表添加到工作簿并命名 XLSX.utils.book_append_sheet(workbook worksheet “员工信息表”); // 5. 写入文件 const outputPath path.join(__dirname ‘output_with_style.xlsx’); XLSX.writeFile(workbook outputPath); console.log(Excel文件已生成${outputPath});运行node writeExcel.js你会在目录下看到一个output_with_style.xlsx文件。用Excel或WPS打开它你会发现表头加粗居中了列宽合适了而且李四的工龄是4年他的“工龄”单元格被标上了浅绿色背景。生成环节的深度解析与避坑指南json_to_sheet的局限性这个函数非常方便但它只处理数据。所有的样式字体、颜色、边框、数字格式都需要事后通过操作worksheet对象的单元格s属性来添加。s属性的结构遵循Excel的样式对象规范学习起来有点成本。样式设置的复杂性上面的例子只是设置了字体加粗、对齐和填充色。更复杂的样式如边框、数字格式如货币、百分比、公式等需要构造更复杂的s对象。例如将“入职日期”列设置为“YYYY年MM月DD日”格式// 假设入职日期在D列索引3 for (let R range.s.r 1; R range.e.r; R) { const cellAddress XLSX.utils.encode_cell({ r: R c: 3 }); const cell worksheet[cellAddress]; if (cell) { cell.z ‘yyyy”年”mm”月”dd”日”;’; // Excel的数字格式代码 // 注意z是数字格式代码s是样式对象。有时需要同时设置。 } }Excel的数字格式代码是个单独的领域需要查文档。xlsx库的官方文档和源码中的/bits/90_ssf.js文件有一些例子。“!ref”的重要性worksheet[‘!ref’]定义了工作表的数据范围比如A1:E4。在添加样式或遍历单元格时一定要先解码这个范围XLSX.utils.decode_range否则可能会漏掉一些单元格或者访问不存在的单元格导致错误。写入性能XLSX.writeFile是同步的对于大文件会阻塞。对于服务器应用可以考虑使用XLSX.write生成Buffer然后用fs的异步API写入或者使用流虽然xlsx对流支持不直接但可以分片生成。对于超大数据量数十万行可能需要考虑exceljs或node-xlsx-writer这类支持流式写入的库。6. 高级应用多工作表、公式与文件流处理在实际项目中需求往往更复杂。我们可能需要生成包含多个工作表的报表或者在单元格里插入公式甚至处理用户上传的Excel流。6.1 创建包含多个工作表的工作簿假设我们要生成一个报表包含“数据总览”和“城市统计”两个Sheet。const XLSX require(‘xlsx’); // 数据总览Sheet的数据 const overviewData [ /* … 员工数据 … */ ]; // 城市统计Sheet的数据来自之前的聚合计算 const citySummaryData Object.entries(cityStats).map(([city stats]) ({ ‘城市’: city ‘员工数量’: stats.count ‘平均年龄’: stats.averageAge })); const workbook XLSX.utils.book_new(); // 创建第一个工作表 const overviewSheet XLSX.utils.json_to_sheet(overviewData); XLSX.utils.book_append_sheet(workbook overviewSheet “数据总览”); // 创建第二个工作表 const summarySheet XLSX.utils.json_to_sheet(citySummaryData); XLSX.utils.book_append_sheet(workbook summarySheet “城市统计”); // 甚至可以创建一个只包含图表链接或说明的Sheet const infoSheet XLSX.utils.aoa_to_sheet([ // aoa_to_sheet: Array of Arrays 转工作表 [‘报表生成说明’] [‘生成时间’ new Date().toLocaleString()] [‘数据来源’ ‘内部数据库’] [] [‘注城市统计表数据来源于数据总览表的聚合计算。’] ]); XLSX.utils.book_append_sheet(workbook infoSheet “说明”); XLSX.writeFile(workbook ‘multi_sheet_report.xlsx’);6.2 在单元格中插入公式Excel的公式是其灵魂。xlsx库支持写入公式但不会计算公式的结果。公式的计算需要由Excel客户端如Microsoft Excel WPS在打开文件时执行。const worksheet XLSX.utils.aoa_to_sheet([ [‘项目’ ‘预算’ ‘实际’ ‘差额’] [‘A项目’ 10000 9500] [‘B项目’ 20000 21000] [‘总计’ ] // 总计行前两列空差额列待计算 ]); // 在D2单元格A项目差额写入公式 B2-C2 worksheet[‘D2’] { f: ‘B2-C2’ t: ‘n’ }; // f 表示公式(forumula) // 在D3单元格B项目差额写入公式 B3-C3 worksheet[‘D3’] { f: ‘B3-C3’ t: ‘n’ }; // 在B4单元格预算总计写入公式 SUM(B2:B3) worksheet[‘B4’] { f: ‘SUM(B2:B3)’ t: ‘n’ }; // 在C4单元格实际总计写入公式 SUM(C2:C3) worksheet[‘C4’] { f: ‘SUM(C2:C3)’ t: ‘n’ }; // 在D4单元格差额总计写入公式 SUM(D2:D3) 或者 B4-C4 worksheet[‘D4’] { f: ‘D2D3’ t: ‘n’ }; // 注意公式字符串需要符合Excel语法 const wb XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb worksheet “带公式”); XLSX.writeFile(wb ‘with_formula.xlsx’);打开这个文件你会看到单元格显示公式点击单元格编辑栏会显示B2-C2这样的公式。只有当你在Excel/WPS里打开或者手动触发计算后才会显示计算结果。6.3 处理文件上传流Web应用场景在Express或Koa这样的Web框架中用户通过表单上传Excel文件。我们通常使用multer这样的中间件来接收文件它提供的是文件在服务器上的路径或Buffer。const express require(‘express’); const multer require(‘multer’); const XLSX require(‘xlsx’); const app express(); const upload multer({ dest: ‘uploads/’ }); // 文件暂存目录 app.post(‘/upload’ upload.single(‘excelFile’) (req res) { if (!req.file) { return res.status(400).json({ error: ‘请上传文件’ }); } try { // req.file.path 是multer保存的临时文件路径 const workbook XLSX.readFile(req.file.path); const worksheet workbook.Sheets[workbook.SheetNames[0]]; const data XLSX.utils.sheet_to_json(worksheet); // … 处理你的数据逻辑 … // 处理完后可以删除临时文件可选 const fs require(‘fs’); fs.unlink(req.file.path (err) { if (err) console.error(‘删除临时文件失败’ err); }); res.json({ success: true data: data /* 或处理后的结果 */ }); } catch (error) { console.error(‘处理上传文件时出错’ error); res.status(500).json({ error: ‘文件处理失败’ }); } }); // 生成并提供Excel文件下载 app.get(‘/download’ (req res) { const data [ /* … 从数据库或其他地方获取数据 … */ ]; const worksheet XLSX.utils.json_to_sheet(data); const workbook XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook worksheet “Sheet1”); // 生成二进制Buffer const excelBuffer XLSX.write(workbook { bookType: ‘xlsx’ type: ‘buffer’ }); // 设置HTTP响应头告诉浏览器这是一个要下载的Excel文件 res.setHeader(‘Content-Disposition’ ‘attachment; filename”report.xlsx”’); res.setHeader(‘Content-Type’ ‘application/vnd.openxmlformats-officedocument.spreadsheetml.sheet’); res.send(excelBuffer); // 发送Buffer }); app.listen(3000 () console.log(‘服务器运行在端口3000’));这里的关键是上传使用XLSX.readFile读取服务器上的临时文件。下载使用XLSX.write的{ type: ‘buffer’ }选项生成二进制Buffer然后通过HTTP响应直接发送给前端。前端浏览器接收到这种响应头和二进制数据就会触发文件下载。7. 实战踩坑与性能优化经验谈用了这么多年xlsx坑没少踩。下面分享几个最常见的以及对应的解决方案。坑1读取日期变成数字有时候你读取一个日期单元格得到的v值是一个像44927这样的数字。这是因为Excel内部用“序列日期”系统存储日期从1899-12-30或1900-01-01开始计算的天数。xlsx库的sheet_to_json默认会尝试转换但如果单元格格式不是明确的日期格式或者库没识别出来就会返回原始数字。解决方案使用XLSX.utils.sheet_to_json(worksheet { raw: false dateNF: ‘YYYY-MM-DD’ })。raw: false会尝试让库输出格式化后的字符串w属性dateNF可以指定你想要的日期格式字符串。手动转换如果知道该列是日期可以读取后手动转换。xlsx提供了一个工具函数XLSX.SSF.parse_date_code(num)可以把那个数字转换成{ y m d … }对象。const excelDateNum 44927; const dateObj XLSX.SSF.parse_date_code(excelDateNum); // dateObj 返回 { y: 2023 m: 1 d: 15 … } 注意m是从1开始的 const jsDate new Date(dateObj.y dateObj.m - 1 dateObj.d);坑2大文件内存溢出当你尝试用XLSX.readFile读取一个几百MB的Excel文件时Node.js进程可能会因为内存不足而崩溃。解决方案流式读取部分支持xlsx库的XLSX.read函数可以接受一个type: ‘file’的选项并配合cellStyles: falsecellDates: false等选项来减少内存占用但它本质上还是会把文件内容读入内存。对于纯数据的大文件可以尝试关闭样式解析。const workbook XLSX.readFile(‘huge.xlsx’ { cellStyles: false // 不解析样式 cellDates: false // 不尝试转换日期 sheetStubs: false // 不保留空单元格存根 });换库如果文件真的巨大考虑使用exceljs它支持流式读取reader.stream和流式写入writer.stream可以分块处理数据对内存友好。预处理如果可能让上游系统导出.csv格式用Node.js的fs.createReadStream和csv-parser等流式CSV解析器处理这是处理海量数据最有效的方式。坑3合并单元格的处理如果你的Excel有合并单元格sheet_to_json默认行为可能会让你丢失数据。例如A1:A3合并了值为“总计”那么转换后只有第一行有这个值A2和A3的位置可能是null或undefined。解决方案读取合并单元格信息worksheet[‘!merges’]数组包含了所有合并单元格的范围。你需要自己写逻辑在转换JSON时将合并区域的值“填充”到每个对应的单元格位置。使用XLSX.utils.sheet_to_json(worksheet { header: 1 raw: true })获取二维数组然后自己遍历!merges来填充二维数组最后再转换成你需要的对象格式。这个过程有点繁琐但对于保持数据结构完整是必要的。坑4生成的文件用WPS打开提示“文件已损坏”或自动改后缀对应热搜词wps后台自动改xlsx到xlsm。这个问题我遇到过好几次。通常不是因为xlsx库生成的文件真的坏了而是因为文件里包含了一些WPS不兼容或谨慎对待的特性比如宏、特定的公式函数、或某些样式的定义方式。WPS出于安全考虑可能会将其另存为.xlsm启用宏的工作簿或提示修复。解决方案检查内容确保你没有无意中写入了xlsx不原生支持的复杂特性。最简单的测试方法是用Microsoft Excel打开你生成的文件看是否正常。简化文件如果只是需要数据尝试生成时去掉所有样式s属性、公式f属性只保留纯数据。使用bookType选项XLSX.writeFile支持不同的bookType如‘xlsx’‘xlsm’‘xlsb’‘ods’等。如果你确定需要宏可以显式指定bookType: ‘xlsm’。但xlsx库本身不支持创建VBA宏。用户教育在下载链接旁加个备注“建议使用Microsoft Excel 2007以上版本或新版WPS打开”。性能优化小技巧批量操作单元格样式如果要对整行或整列设置样式避免在循环里单个单元格设置s属性。可以先构建一个样式对象然后批量赋值。或者对于大量相同样式的单元格考虑使用“默认行/列样式”但这在xlsx库中实现起来比较麻烦。善用sheet_to_json的选项defval参数可以设置默认值blankrows可以控制是否跳过空行。合理使用它们可以简化后续的数据清洗逻辑。对于只读场景如果只需要读取特定列或特定区域的数据可以使用XLSX.utils.sheet_to_json(worksheet { range: ‘A1:C10’ })来限定范围减少不必要的数据解析。最后xlsx库的文档在GitHub仓库的README和docbits/目录下是宝库里面有很多高级用例和API说明。遇到奇怪的问题先去翻翻源码和文档往往能找到答案。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门