JavaScript怎么读取Excel指定Sheet或单元格范围
详细讲解在浏览器端与Node.js中使用JavaScript精确读取Excel指定工作表(Sheet)以及指定单元格范围(Range)的实现方法、代码示例与核心原理。
在JavaScript中读取Excel指定Sheet或单元格范围,核心依靠将工作簿解析为对象树后进行两级过滤:第一级通过 workbook.Sheets[sheetName] 按名称或索引精确定位工作表;第二级在调用 XLSX.utils.sheet_to_json 时传入 range 配置参数(支持 "B2:E10" 字符串格式或坐标对象),直接截取目标区域,避免全表扫描带来的内存浪费与脏数据干扰。
一、基础准备:解析 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);
二、指定读取特定 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];
三、指定单元格范围(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
});
方案 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 ]
// ]
五、常见问题与注意事项
字符串形式(如
"A1"、"B2")严格对应 Excel 界面看到的行列标号,从 1 开始;而通过对象 { s: { c, r }, e: { c, r } } 或直接传入数字行号时,全部从 0 开始。例如第 1 行对应 r: 0,B 列对应 c: 1。
在 Excel 中如果将 B2:C3 进行了单元格合并,SheetJS 解析时真实数据仅存放在左上角单元格
B2 中,而 B3、C2、C3 对应的值均为 undefined。如果使用 sheet_to_json 提取,后续空单元格将被忽略或填充默认值。若需获取合并信息,需检查 worksheet['!merges'] 数组。
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} 条记录`);
JS大数值转字符串出现科学计数法怎么办
2026-11-05 17:09:21
JS split切割后出现空字符串怎么过滤
2026-11-05 16:00:30
在 React 或 Vue 项目中封装使用 JS 节流防抖
2026-11-05 15:27:29
JavaScript中怎么将带小数的字符串转为整数?
2026-11-05 14:32:38
怎么用JavaScript策略模式重构电商促销计算逻辑?
2026-11-05 14:01:09








