Skip to content

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;
    }
}