You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用SheetJS生成的XLSX文件无法读取单元格值问题排查

问题分析与解决思路

首先,你的问题核心在于SheetJS读取原生Excel和自生成Excel时,单元格对象的结构差异,加上代码里的小瑕疵导致了崩溃或逻辑失效。具体来说:

  1. raw: true读取选项的坑:你在读取文件时设置了raw: true,这个选项会让SheetJS直接返回单元格的原始值(比如数字、字符串),而不是包含.v.t等属性的完整单元格对象。但原生Excel文件的单元格结构可能保留了完整对象,而自生成的文件再读取时,raw: true会让ws["C9"]变成纯值,此时访问.v就会抛出Cannot read property 'v' of undefined或类型错误,直接崩溃。

  2. 数组语法错误:你定义dataRange时多了一个逗号(ws["C11"].v, , ws["C12"].v),这会在数组中插入一个undefined元素,循环时判断undefined > 0会引发逻辑异常。

  3. 空单元格未做判断:当单元格为空时,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

火山引擎 最新活动