使用SheetJS生成的XLSX文件无法读取单元格值问题排查
首先,你的问题核心在于SheetJS读取原生Excel和自生成Excel时,单元格对象的结构差异,加上代码里的小瑕疵导致了崩溃或逻辑失效。具体来说:
raw: true读取选项的坑:你在读取文件时设置了raw: true,这个选项会让SheetJS直接返回单元格的原始值(比如数字、字符串),而不是包含.v、.t等属性的完整单元格对象。但原生Excel文件的单元格结构可能保留了完整对象,而自生成的文件再读取时,raw: true会让ws["C9"]变成纯值,此时访问.v就会抛出Cannot read property 'v' of undefined或类型错误,直接崩溃。数组语法错误:你定义
dataRange时多了一个逗号(ws["C11"].v, , ws["C12"].v),这会在数组中插入一个undefined元素,循环时判断undefined > 0会引发逻辑异常。空单元格未做判断:当单元格为空时,
ws["C9"]可能不存在或值为undefined,直接访问.v会报错。
修复方案
1. 调整读取选项,保留完整单元格对象
移除raw: true选项,让SheetJS返回带.v(值)、.t(类型)等属性的完整单元格对象,这样原生和自生成文件的读取结构就一致了:
var wb = XLSX.read(data, { type: 'array', cellDates: true, cellStyles: true, sheetStubs: true });
2. 重构findEmpty函数,增加安全校验
修复语法错误,同时增加单元格存在性、值有效性的判断,避免访问undefined属性:
function findEmpty() { // 用数组存储要检查的单元格地址,避免手写重复代码 const targetCells = ["C9", "C10", "C11", "C12", "C13", "C14", "C15", "C16", "C17", "C18", "C19", "C20", "C21", "C22", "C23", "C24", "C25"]; let dataSize = 0; for (const cellAddr of targetCells) { const cell = ws[cellAddr]; // 先检查单元格是否存在,且有有效值 if (cell && cell.v !== undefined) { // 转换为数字,兼容字符串形式的数字(比如用户输入的"123") const cellValue = Number(cell.v); // 确保是有效数字且大于0 if (!isNaN(cellValue) && cellValue > 0) { dataSize++; } } } // C9对应第9行(0索引为8),所以第一个空行是8 + 已填充行数 return 8 + dataSize; }
3. 验证写入逻辑的一致性
XLSX.utils.sheet_add_json默认会生成带.v属性的单元格对象,和原生Excel的结构匹配,所以不需要修改写入代码,但要确保传入的origin参数正确(你当前的firstEmpty计算是正确的,因为origin接受0-based的行索引)。
完整修复后的代码片段
$('#xlf').change(function(e) { var reader = new FileReader(); reader.readAsArrayBuffer(e.target.files[0]); reader.onload = function(e) { var data = new Uint8Array(reader.result); // 移除raw: true,保留完整单元格对象 var wb = XLSX.read(data, { type: 'array', cellDates: true, cellStyles: true, sheetStubs: true }); var fSN = wb.SheetNames[0]; var ws = wb.Sheets[fSN]; function findEmpty() { const targetCells = ["C9", "C10", "C11", "C12", "C13", "C14", "C15", "C16", "C17", "C18", "C19", "C20", "C21", "C22", "C23", "C24", "C25"]; let dataSize = 0; for (const cellAddr of targetCells) { const cell = ws[cellAddr]; if (cell && cell.v !== undefined) { const cellValue = Number(cell.v); if (!isNaN(cellValue) && cellValue > 0) { dataSize++; } } } return 8 + dataSize; } var firstEmpty = findEmpty(); var header = ["a", "b", "c", "d", "e", "f"]; XLSX.utils.sheet_add_json(ws, [{ a: $('#from').val(), b: $('#to').val(), c: $("#differenza").val(), e: $("#comm").val() }], { header: header, origin: firstEmpty, skipHeader: true }); XLSX.writeFile(wb, "test.xlsx"); } });
额外建议
- 可以给
findEmpty函数加个边界判断,比如当所有单元格都大于0时,确保不会超出C25的范围(比如限制dataSize最大为17,因为C9到C25共17个单元格)。 - 如果需要兼容字符串形式的数字(比如用户输入的文本类型数字),
Number(cell.v)的转换逻辑已经覆盖了这种情况。
内容的提问来源于stack exchange,提问作者Agricolo




