工作中经常遇到让前端来识别Excel的内容,以此来进行校验,那么首先如何获取Excel的内容就是一个问题,百度了一下发现用SheetJS开源的JS-XLSX这个工具库居多。它支持xls、xlsx、csv等多种表格格式文件的解析。 官方github:github.com/SheetJS/js-…
Demo图例:
最后期望获取的数据结构:
如何使用XLSX
首先,官网这里的文件只需要用dist目录下的 xlsx.core.min.js 就足够了;xlsx.core.min.js主要是包含了基本的识别解析功能,xlsx.full.min.js则是包含了所有功能模块。然后在所在项目引入即可。
下面演示的案例是在原生JS下写的,所以用的script标签引入的,若是Vue等框架项目可以用 npm 下载引入 XLSX即可。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input type="file" onchange="changes(event)" />
</body>
</html>
// 引入本地的xlsx工具库
<script src="./xlsx.core.min.js"></script>
<script>
const file = document.querySelector('input');
function changes(e) {
if (e.target.files.length > 0) {
// 获取当前的文件名后缀进行判断是否可以解析
const fileName = e.target.files[0].name;
const fileArr = fileName.split('.');
const fileSuffix = fileArr[fileArr.length - 1];
if (fileSuffix === 'xlsx' || fileSuffix === 'xls') {
// 解析数据
fileToJson(e.target.files[0], (sheets) => {
console.log('获取到的表格数据', sheets);
});
} else {
console.log('不支持该格式的解析');
}
} else {
console.log('请选择文件上传');
}
}
</script>
上传文件后,要创建一个FileReader对象,该对象中的方法可以读取文件内容,但是需要经过XLSX解析编译才可以阅读。
FileReader 接口参考地址:developer.mozilla.org/zh-CN/docs/…
根据FileReader对象返回的结果,XLSX通过read方法来解析读取,返回一个WorkBook对象。 read方法接受两个参数,参数一是FileReader返回的数据,参数二(可选)是一个配置对象,
常用的有type和cellDates等字段;type主要取值如下: base64: 以base64方式读取; binary: BinaryString格式(byte n is data.charCodeAt(n)) string: UTF8编码的字符串; buffer: nodejs Buffer; array: Uint8Array,8位无符号数组; file: 文件的路径(仅nodejs下支持); 本例使用了 BinaryString 和 base64 两个格式解析
function fileToJson(file,callback) {
// 数据处理结果
let result;
// 是否用BinaryString(字节字符串格式) 否则使用base64(二进制格式)
let isBinary = true;
// 创建一个FileReader对象
var reader = new FileReader();
// reader在onload解析结束事件时触发
reader.onload = function (e) {
var data = e.target.result;
if (isBinary) {
result = XLSX.read(data, {
type: 'binary',
cellDates: true,// 为了获取excel表格中的时间,返回格式为世界时间
});
} else {
result = XLSX.read(btoa(fixdata(data)), {
type: 'base64',
cellDates: true,
});
}
// 格式化数据
formatResult(result,callback);
};
if (isBinary) {
reader.readAsBinaryString(file);// 使用 BinaryString 来解析文件数据
} else {
reader.readAsArrayBuffer(file);// 使用base64 来解析文件数据
}
}
// 文件流转 base64
function fixdata(data) {
var o = '',
l = 0,
w = 10240;
for (; l < data.byteLength / w; ++l)
o += String.fromCharCode.apply(
null,
new Uint8Array(data.slice(l * w, l * w + w))
);
o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)));
return o;
}
上图就是用XLSX解析后的结果,由于解析成WorkBook的形式,不好读取数据,所以需要使用XLSX的方法XLSX.utils.sheet_to_json
该方法是将WorkBook中的sheet数据转成json,有两个参数:参数一是sheet数据,参数二是配置对象
const sheetJson = XLSX.utils.sheet_to_json(sheets[item], { header: 1 });
// 配置header:1表示生成二维数组,具体配置情况请看SheetJS官网配置
可是获取到的JSON数据没有考虑到合并单元格的情况(如图下),只给合并单元格的首个单元赋值,不是理想的结构格式,所以还需要再处理
因为合并单元格的数据的相关位置XLSX也返回了,所以利用其处理数据结构。
// 将读取的数据转成JSON
function formatResult(data,callback) {
// 获取总数据
const sheets = data.Sheets;
// 获取每个表格
const sheetItem = Object.keys(sheets);
// 返回sheetJSON数据源
let sheetArr = [];
// 获取
sheetItem.forEach((item) => {
const sheetJson = XLSX.utils.sheet_to_json(sheets[item], { header: 1 });
console.log('sheetJson', sheetJson);
// 格式化Item时间数据
formatItemDate(sheetJson);
// 格式化Item合并数据
formatItemMerge(sheets[item], sheetJson);
// 组合数据
sheetArr.push({
name: item,
list: sheetJson
})
});
// 返回数据
callback(sheetArr)
}
// 格式化Item时间数据
function formatItemDate(data) {
data.forEach((row) => {
row.forEach((item, index) => {
// 若有数据为时间格式则格式化时间
if (item instanceof Date) {
// 坑:这里因为XLSX插件源码中获取的时间少了近43秒,所以在获取凌晨的时间上会相差一天的情况,这里手动将时间加上
var date = new Date(Date.parse(item) + 43 * 1000);
row[index] = `${date.getFullYear()}-${String(
date.getMonth() + 1
).padStart(2, 0)}-${String(date.getDate()).padStart(2, 0)}`;
}
});
});
console.log('data', data);
}
// 格式化Item合并数据
function formatItemMerge(sheetItem, data) {
// 保存每一项sheet中的合并单元格记录
const merges = sheetItem['!merges'] || [];
merges.forEach((el) => {
const start = el.s;
const end = el.e;
// 处理行合并数据
if (start.r === end.r) {
const item = data[start.r][start.c];
for (let index = start.c; index <= end.c; index++) {
data[start.r][index] = item;
}
}
// 处理列合并数据
if (start.c === end.c) {
const item = data[start.r][start.c];
for (let index = start.r; index <= end.r; index++) {
data[index][start.c] = item;
}
}
});
}
最后源码的地址: github.com/Cwolf-spiri…