JavaScript怎么读取Excel指定Sheet或单元格范围

作者:函数茶馆时间:2026-11-02 10:22:28浏览:196

详细讲解在浏览器端与Node.js中使用JavaScript精确读取Excel指定工作表(Sheet)以及指定单元格范围(Range)的实现方法、代码示例与核心原理。

在JavaScript中读取Excel指定Sheet或单元格范围,核心依靠将工作簿解析为对象树后进行两级过滤:第一级通过 workbook.Sheets[sheetName] 按名称或索引精确定位工作表;第二级在调用 XLSX.utils.sheet_to_json 时传入 range 配置参数(支持 "B2:E10" 字符串格式或坐标对象),直接截取目标区域,避免全表扫描带来的内存浪费与脏数据干扰。

JavaScript怎么读取Excel指定Sheet或单元格范围
图 0:JavaScript 指定 Sheet 与单元格范围的核心定位机制

一、基础准备:解析 Excel 文件并获取工作簿

无论是运行在浏览器端还是 Node.js 环境,解析 Excel 文件最成熟的标准方案是使用 SheetJS(库名为 xlsx)。它提供了将二进制数据解析为 Workbook 对象的统一能力。在读取具体数据之前,我们首先需要将文件加载为工作簿对象。

1. 浏览器环境读取

在浏览器中,通常通过 <input type="file"> 接收用户上传的文件,使用原生 FileReader 或 File.arrayBuffer() 读取为二进制数组,再调用 XLSX.read 解析:

// 浏览器端示例:通过文件输入框读取
const fileInput = document.getElementById('excelFileInput');

fileInput.addEventListener('change', async (event) => {
  const file = event.target.files[0];
  if (!file) return;

  // 将文件转换为 ArrayBuffer
  const arrayBuffer = await file.arrayBuffer();

  // 解析生成 Workbook 对象
  const workbook = XLSX.read(arrayBuffer, { type: 'array' });

  // 后续进行 Sheet 与范围提取...
  console.log('可用工作表列表:', workbook.SheetNames);
});

2. Node.js 环境读取

在 Node.js 服务端,可以直接使用 XLSX.readFile 通过文件路径读取,或使用 fs.readFileSync 读取 Buffer 后传入:

// Node.js 端示例
const XLSX = require('xlsx');

// 方式一:直接按路径同步读取
const workbook = XLSX.readFile('./sales_data.xlsx');

// 方式二:从 Buffer 读取
// const fs = require('fs');
// const buffer = fs.readFileSync('./sales_data.xlsx');
// const workbook = XLSX.read(buffer, { type: 'buffer' });

console.log('工作表列表:', workbook.SheetNames);
JavaScript怎么读取Excel指定Sheet或单元格范围
图 1:从原始输入流到精确截取输出的完整操作步骤流水线

二、指定读取特定 Sheet(工作表)

一个 Excel 工作簿通常包含多个 Sheet。解析后的 workbook 内部通过两个核心属性维护所有工作表:

  • workbook.SheetNames:一个纯字符串数组,按工作表在 Excel 中的从左到右排序保存所有工作表的名称(如 ['Sheet1', '销售明细', '汇总'])。
  • workbook.Sheets:一个以工作表名称为键、对应 Worksheet 对象为值的键值字典。

1. 按工作表名称精准读取

如果你明确知晓所需工作表的名称(例如业务固定的“月度报表”),可以直接通过键名访问:

const targetSheetName = '月度报表';

// 安全校验:确认目标工作表存在
if (workbook.SheetNames.includes(targetSheetName)) {
  const worksheet = workbook.Sheets[targetSheetName];
  console.log(`成功找到工作表 [${targetSheetName}]`);
} else {
  console.error(`工作表 [${targetSheetName}] 不存在,可用表有:`, workbook.SheetNames);
}

2. 按下标索引读取(如第一个或最后一个 Sheet)

当用户上传的文件工作表名称不固定(例如导出的临时报表名字随日期变动),但固定位于第一张表时,可结合 SheetNames 数组下标索引定位:

// 读取第一个工作表
const firstSheetName = workbook.SheetNames[0];
const firstSheet = workbook.Sheets[firstSheetName];

// 读取最后一个工作表
const lastSheetName = workbook.SheetNames[workbook.SheetNames.length - 1];
const lastSheet = workbook.Sheets[lastSheetName];
JavaScript怎么读取Excel指定Sheet或单元格范围
图 2:工作簿索引映射与单张工作表(Worksheet)的哈希存储结构

三、指定单元格范围(Range)的四种核心方案

默认情况下,若直接执行 XLSX.utils.sheet_to_json(worksheet),SheetJS 会读取整张表(依据 worksheet['!ref'] 声明的全表范围)。实际业务中,表格上方常有大段说明文字、标题合并单元格,或者底部存在合计行。此时必须精确限定读取范围。

方案 1:使用标准 A1 字符串格式传入 range(最直观)

sheet_to_json 的配置项支持直接传入形如 "起始单元格:结束单元格" 的字符串。例如要读取从 B2 到 E10 的矩阵:

const worksheet = workbook.Sheets['销售汇总'];

// 直接使用 A1 范围字符串
const result = XLSX.utils.sheet_to_json(worksheet, {
  range: "B2:E10"
});

console.log('读取指定范围结果:', result);

行为细节:在该模式下,SheetJS 会自动将所选范围的第一行(即 B2 到 E2)作为 JSON 对象的属性名(键),后续各行作为具体数据对象返回。

方案 2:使用起始行偏移行号(跳过前 N 行)

如果不需要限制列,只需跳过前几行的标题或说明文本,可以直接给 range 传入一个数字。注意:SheetJS 内部从 0 开始计数行号!

// 假设第 1、2 行是文档标题与副标题,真实的表头在 Excel 第 3 行
// 传入 2 即代表跳过 0 和 1 两行,从第 3 行(索引 2)开始作为表头读取
const result = XLSX.utils.sheet_to_json(worksheet, {
  range: 2 // 0-based 索引,代表从 Excel 的第 3 行开始读取整表
});

方案 3:使用坐标对象 { s, e } 动态计算范围

当行号或列号是通过业务逻辑动态计算得出时,使用对象结构比手动拼接字符串更可靠。对象包含 s(Start 起点)与 e(End 终点),每个点包含 r(Row 行索引,从 0 开始)与 c(Column 列索引,从 0 开始):

// 读取从第 2 行第 2 列(Excel 中的 B2)到第 15 行第 5 列(Excel 中的 E15)
// 转换规则:B列 -> 1,E列 -> 4;第2行 -> 1,第15行 -> 14
const customRange = {
  s: { c: 1, r: 1 },  // 起点:B2 (col=1, row=1)
  e: { c: 4, r: 14 }  // 终点:E15 (col=4, row=14)
};

const result = XLSX.utils.sheet_to_json(worksheet, {
  range: customRange
});
JavaScript怎么读取Excel指定Sheet或单元格范围
图 3:Excel 界面可见坐标与 SheetJS 内部 0 索引坐标对象的精确换算

方案 4:通过底层工具函数动态解析并逐个单元格提取

如果你不仅需要获取单元格文字,还需要精确提取单元格公式、原始类型(数字、日期、布尔),或者处理不规则的零散范围,可以使用 XLSX.utils.decode_range 解析范围,通过双层循环按坐标逐个读取:

// 1. 将 A1 字符串解析为内部边界对象
const range = XLSX.utils.decode_range("B2:D4");

// 2. 遍历指定范围内的每一行、每一列
for (let r = range.s.r; r <= range.e.r; ++r) {
  for (let c = range.s.c; c <= range.e.c; ++c) {
    // 根据行列索引编码为单元格地址(如 {c: 1, r: 1} -> "B2")
    const cellAddress = XLSX.utils.encode_cell({ c, r });
    const cell = worksheet[cellAddress];

    if (cell) {
      console.log(`单元格 [${cellAddress}]:`, {
        value: cell.v,       // 原始值
        formatted: cell.w,   // 格式化后的文本(若有)
        type: cell.t         // 类型: 'n' 数值, 's' 文本, 'b' 布尔, 'd' 日期
      });
    } else {
      console.log(`单元格 [${cellAddress}] 为空`);
    }
  }
}

四、数据提取形态控制与输出格式选项

在调用 XLSX.utils.sheet_to_json 时,除了 range 限制边界外,结合以下几个核心参数可以灵活控制提取结果的数据结构:

配置选项 典型取值 实际作用与效果
range "B2:E10" 或 { s, e } 限制仅读取指定矩形范围内的单元格。
header: 1 数值 1 输出为二维数组(每行一个数组),不把第一行当作对象属性名,适合纯矩阵数据。
header: [...] ['id', 'name', 'score'] 自定义对象属性名,直接将各列映射到指定业务字段中。
defval null 或 "" 为空白单元格指定默认填充值,防止生成的 JSON 遗漏空字段属性。
raw false / true (默认) 设为 false 时读取格式化后的文本字符串(如百分比、带逗号的千分位)。

例如,读取没有标准表头、只需矩阵数组的区域:

// 提取指定区域并输出为纯二维矩阵(二维数组)
const matrix = XLSX.utils.sheet_to_json(worksheet, {
  range: "B3:D6",
  header: 1,      // 关键参数:强制输出二维数组
  defval: null    // 空单元格填充 null
});

console.log(matrix);
// 输出示例:
// [
//   [ "商品A", 299, 10 ],
//   [ "商品B", 129, 35 ],
//   [ "商品C", 89,  null ]
// ]
JavaScript怎么读取Excel指定Sheet或单元格范围
图 4:针对不同业务诉求的提取策略与输出格式决策指引

五、常见问题与注意事项

避坑要点 1:索引从 0 开始还是 1 开始?
字符串形式(如 "A1"、"B2")严格对应 Excel 界面看到的行列标号,从 1 开始;而通过对象 { s: { c, r }, e: { c, r } } 或直接传入数字行号时,全部从 0 开始。例如第 1 行对应 r: 0,B 列对应 c: 1。
避坑要点 2:合并单元格(Merged Cells)的数据只存在于左上角
在 Excel 中如果将 B2:C3 进行了单元格合并,SheetJS 解析时真实数据仅存放在左上角单元格 B2 中,而 B3、C2、C3 对应的值均为 undefined。如果使用 sheet_to_json 提取,后续空单元格将被忽略或填充默认值。若需获取合并信息,需检查 worksheet['!merges'] 数组。
避坑要点 3:日期格式读取出现五位整数
Excel 底层将日期存储为自 1900 年以来的序列号天数(如 45200)。如果直接读取可能得到纯数字。在调用 XLSX.read 时加上 cellDates: true 选项,SheetJS 会自动将其转化为原生的 JavaScript Date 对象:
const wb = XLSX.read(data, { type: 'array', cellDates: true });

六、总结完整代码清单

下面给出结合上述所有知识点的一个完整通用读取函数。该函数支持直接传入工作表标识(名称或下标)以及可选的单元格范围:

/**
 * 通用 Excel 指定工作表与范围读取函数
 * @param {ArrayBuffer|Buffer} fileData 二进制数据
 * @param {string|number} sheetKey 工作表名称或下标(从0开始)
 * @param {string|object} [targetRange] 单元格范围,如 "B2:E10" 或 {s:{c:1,r:1}, e:{c:4,r:9}}
 * @returns {Array<object>} 转换后的数据对象数组
 */
function readExcelRange(fileData, sheetKey, targetRange) {
  // 1. 解析工作簿
  const workbook = XLSX.read(fileData, {
    type: typeof Buffer !== 'undefined' && Buffer.isBuffer(fileData) ? 'buffer' : 'array',
    cellDates: true // 自动解析日期对象
  });

  // 2. 根据类型匹配 Sheet 名称
  let sheetName = '';
  if (typeof sheetKey === 'number') {
    sheetName = workbook.SheetNames[sheetKey];
  } else if (typeof sheetKey === 'string') {
    sheetName = sheetKey;
  }

  // 校验 Sheet 是否存在
  if (!sheetName || !workbook.Sheets[sheetName]) {
    throw new Error(`未找到工作表: ${sheetKey},当前所有可用表: ${workbook.SheetNames.join(', ')}`);
  }

  const worksheet = workbook.Sheets[sheetName];

  // 3. 配置读取选项并提取
  const options = {
    defval: null // 保证缺失字段输出为 null 而不被丢弃
  };

  if (targetRange) {
    options.range = targetRange;
  }

  return XLSX.utils.sheet_to_json(worksheet, options);
}

// 调用示例:
// const data = readExcelRange(buffer, '销售明细', 'B2:F20');
// console.log(`读取完成,共获取 ${data.length} 条记录`);
作者最新文章
相关文章
更多