主题
Exceljs上传解析Excel的时候遇到的坑
一. Exceljs上传解析Excel的时候遇到的坑
坑1. 当一个单元格里面出现不同字体或字号,exceljs解析后,会将这个单元格的值变成富文本,得到的式一个对象,所不能直接取值(包括表头)
js
// 某个单元格的值
{
richText: [
{ text: 'This is '},
{font: {italic: true}, text: 'italic'},
]
}
坑2. 当某个单元格输入一个网址,或被单元格设置成超链接后, 在取值的时候, 得到的也是一个对象(包括表头)
js
{
text: 'www.mylink.com',
hyperlink: 'http://www.mylink.com', // 会自动补全协议
tooltip: 'www.mylink.com'
}
3. 坑3. 当某个单元格输入多个\
的时候,单元格会变成超链接,比如\\\
,此时应该将这个单元格当作超链接来对待(包括表头)
4. 当单元格(包括表头)的值全是\
的时候,或者以\
的时候,若是\
的数量为基数个,则最后一个\
会作为转义字符,把js中结尾的引号'
给转移,若我们将这个值以字符串的形式存入数据库,那么值将会变成[object Object]
js
{
text: '\\\',
hyperlink: '\\\', // 会自动补全协议
}
5. Excel上传的文件里面若是存在下拉框这种交互,在解析文件的时候若是用常规的读取方式,会导致读取文件很慢很慢, 这种情况建议通过流式I/O的方式
js
// 常规的读取方式 ---- 遇到Excel中存在下拉框交互时候,读取会很慢很慢
const workbook = new Excel.Workbook();
await workbook.xlsx.read(stream);
解决方案, 用流式I/O
js
const Excel = require('exceljs');
async function parseExcel2(stream, headNameMap) {
const workbook = new Excel.stream.xlsx.WorkbookReader(stream);
let data = [];
let headSource = Object.values(headNameMap);
for await (const worksheetReader of workbook) {
let index = 0;
let headKeys = [];
for await (const row of worksheetReader) {
let rowArr = row.values.slice(1);
if (index === 0) {
rowArr.forEach(item => {
if (headSource.includes(item)) {
Object.entries(headNameMap).forEach(([key, h]) => {
if (getcellVal(item) === h) {
headKeys.push(key);
}
});
} else {
headKeys.push(null);
}
});
index += 1;
} else {
let itemObj = {};
headKeys.forEach((item, idx) => {
if (item !== null) {
itemObj[item] = getcellVal(rowArr[idx]);
}
});
data.push(itemObj);
index += 1;
}
}
}
return data;
}
getcellVal
方法见下面
二. 解决方案
封装一个方法, 在解析Excel的时候,用于取值
js
function getcellVal(obj) {
if (Object.prototype.toString.call(obj) === '[object Object]' && Array.isArray(obj.richText)) {
return obj.richText.reduce((pre, cur) => {
pre += cur.text;
return pre;
}, '').trim().replace('/\\/g', '\\\\');
} else if (Object.prototype.toString.call(obj) === '[object Object]' && 'text' in obj && 'hyperlink' in obj) {
return obj.text.trim().replace('/\\/g', '\\\\');
} else if (typeof obj === 'string' && obj) {
return obj.trim().replace('/\\/g', '\\\\');
} else {
return obj;
}
}