VUE+elementUI+XLSX实现excel文件预览及下载

5,497 阅读5分钟

安装并引入XLSX

npm i xlsx

读取excle文件并解析详细文件信息

选择文件并上传

<el-upload action="/" accept=".xls,.xlsx" :http-request="uploadSuccess">
    <el-button size="mini" type="primary">文件上传</el-button>
</el-upload>
const uploadSuccess = (params: { file: File }) => {
    const fileType = file.name.substring(file.name.lastIndexOf('.') + 1);
    if (fileType !== 'xls' && fileType !== 'xlsx') {
        window.$message.warning('请按照指定模板格式上传');
        return ;
    } else {
        readExcel(file); // 读取文件内容
    }
}

FileReader读取文件内容

interface SheetInfo {
    sheetName: string;
    sheet: Array<string>;
    json: Array<string>;
    tableAu: string;
}
const readExcel = (file: File): Promise<SheetInfo[]> {
    return new Promise((resolve) => {
        const reader = new FileReader();
        reader.onload = (e: any) => {
            const data = reader.result;
            // reader.result === e.target.result
            const result: Array<SheetInfo> = [];
            parseExcelData(data)
            resolve(result);
        };
        reader.readAsArrayBuffer(file);
    });
}

FileReader的读取方法

  • 【属性】readyState 表示FileReader状态的数字 {EMPTY: 0, LOADING: 1, DONE: 2}。
  • 【属性】result 文件的内容。该属性仅在读取操作完成后才有效。
  • 【事件】onload 在读取操作完成时触发。
  • 【方法】readAsArrayBuffer() 读取指定的 Blob 或 File 对象。一旦完成,会出发onload/onloadend事件,readyState状态会变味DONE,同时 result 属性中将包含一个 ArrayBuffer 对象以表示所读取文件的数据。
  • 【方法】readAsBinaryString() 和 readAsArrayBuffer 功能相同,区别是 result 属性将包含所读取文件原始二进制格式。(非标准:  该特性是非标准的,请尽量不要在生产环境中使用它!)
  • 【方法】readAsDataURL() 和 readAsArrayBuffer 功能相同,区别是 result 属性将包含所读取文件的 data:URL格式的字符串(base64编码)。
  • 【方法】readAsText() 异步读取指定的 Blob 或 File 对象,根据特殊的编码格式转化为内容(字符串形式)。

XLSX解析文件内容

import XLSX from 'xlsx';

const parseExcelData = () => {
    const workbook = XLSX.read(data, { type: 'buffer' });
    workbook.SheetNames.forEach(sheetName => {
        const tableAu = XLSX.utils.sheet_to_html(wb.Sheets[sheetName]);
        result.push({
            sheetName,
            sheet: XLSX.utils.sheet_to_formulae(wb.Sheets[sheetName]),
            json: XLSX.utils.sheet_to_json(wb.Sheets[sheetName]),
            tableAu
        });
    });
}

XLSX

解析方法

  • XLSX.read(data, read_opts) 解析数据 data
  • XLSX.readFile(filename, read_opts) 解析文件名为 fileName 的文件,node环境下使用

read_opts

1. type 文件类型

typeexpected input
"base64"string: Base64 encoding of the file
需要将内容转成 base-64 编码的 ASCII 字符串
"binary"string: binary string (byte n is data.charCodeAt(n))
对应 fileReader.readAsBinaryString()
"string"string: JS string (characters interpreted as UTF8)
"buffer"nodejs Buffer
对应 fileReader.readAsArrayBuffer()
"array"array: array of 8-bit unsigned int (byte n is data[n])
Uint8Array,8位无符号数组;
"file"string: path of file that will be read (nodejs only)
文件的路径(仅nodejs下支持);

解析type: base64

1. buffer -> BinaryString -> base64

function fixdata(data) { //文件流转BinaryString
    let o = "", l = 0;
    const 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;
}

const fileReader = new FileReader();
fileReader.onload = (e: any) => {
    const data = fileReader.result;
    const workbook = XLSX.read(btoa(fixdata(data)), {type:"base64"})
    // TODO: ...
};
fileReader.readAsArrayBuffer(file);
  1. fixdata() 将文件流 fileReader.readAsArrayBuffer() 获取搭配的文件流转为二进制字符串。
  2. btoa() 将二进制字符串转化为 base-64 编码的 ASCII 字符串。
  3. XLSX.read(encodedData, {type:"base64"}) 读取excel文件,获取workbook对象

image.png

2. base64文件

const fileReader = new FileReader();
fileReader.onload = (e: any) => {
    const data = fileReader.result;
    const workbook = XLSX.read((data as string).split(';')[1].replace('base64,',''), {type:"base64"});
    // TODO: ...
};
fileReader.readAsDataURL(file);

解析 type: buffer

const reader = new FileReader();
fileReader.readAsArrayBuffer(file)
reader.onload = (e: any) => {
    const data = reader.result;
    const workbook = XLSX.read(data, { type: 'buffer' });
    // TODO: ...
}

解析 type: binary

const reader = new FileReader();
fileReader.readAsBinaryString(file)
reader.onload = (e: any) => {
    const data = reader.result;
    const workbook = XLSX.read(data, { type: 'binary' });
    // TODO: ...
}

excel数据预览

workbook 对象

image.png

  • SheetNames 存放excel中sheet名称
  • Sheets 存放每个sheet中的具体内容

sheet object 表格对象

Sheets 每个对象代表一张表格,表格中除了以 ! 开头的都表示单元格,以 ! 开头的表示一些特殊的含义:

  • !ref 表示所有单元格范围
  • !margin 表示页边距,包含left、right、top、bottom、header、footer
  • !merges 表示合并单元格,是个数组,每个元素包含se两个对象分别表示开始结束se包含rc两个对象分别表示 image.png

cell object 单元格对象

Cell Object 每一个单元格是一个对象,包括 vwtfFrhczls 字段

  • t:表示内容类型,b Boolean, e Error, n Number, d Date, s Text等
  • v:表示原始值
  • w:表示格式化后的内容
  • f:表示公式,例如 f: "C3+4"
  • h:html内容
  • r:富文本内容rich text

XLSX.utils

XLSX.utils 提供了直接可用的API解析

Exporting:

  • XLSX.utils.sheet_to_csv 生成CSV格式
  • XLSX.utils.sheet_to_txt 生成文本格式
  • XLSX.utils.sheet_to_json 输出json格式
  • XLSX.utils.sheet_to_html 将excel数据转化为html(table)
  • XLSX.utils.sheet_to_formulae 输出公式列表(带有值回退)。 image.png

下载文件

  1. XLSX.write 生成一个 sheet
  2. new Blob 将 binary 字符串转成 blob 对象
  3. URL.createObjectURL 将 blob 对象转成 url 下载地址
// 将一个sheet转成最终的excel文件的blob对象,然后利用URL.createObjectURL下载
function sheet2blob(workbook) {
    // 生成excel的配置项
    var wopts: any = {
        bookType: 'xlsx', // 要生成的文件类型
        bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
        type: 'binary'
    };
    var wbout = XLSX.write(workbook, wopts);
    var blob = new Blob([s2ab(wbout)], {type:"application/octet-stream"});
    // 字符串转ArrayBuffer
    function s2ab(s: any) {
        var buf = new ArrayBuffer(s.length);
        var view = new Uint8Array(buf);
        for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
        return buf;
    }
    return blob;
}

参考文章 JS弹出下载对话框以及实现常见文件类型的下载

/**
 * 通用的打开下载对话框方法,没有测试过具体兼容性
 * @param url 下载地址,也可以是一个blob对象,必选
 * @param saveName 保存文件名,可选
 */
export function downloadDialog(sheetDatas, saveName = '导出.xlsx') {
    let url: any = sheet2blob(sheetDatas);
    
    if (typeof url == 'object' && url instanceof Blob) {
        url = URL.createObjectURL(url); // 创建blob地址
    }
    var aLink = document.createElement('a');
    aLink.href = url;
    aLink.download = saveName; // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
    var event;
    if (window.MouseEvent) event = new MouseEvent('click');
    else {
        event = document.createEvent('MouseEvents');
        event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    }
    aLink.dispatchEvent(event);
}

workbook 对象

workbook 的格式和 XLSX.read 读取出来的对象格式一致,包含 SheetNames 和 Sheets

👇 通过XLSX.utils提供的API生成workbook

image.png

XLSX.utils

XLSX.utils 提供了直接可用的API解析

Importing:

  • XLSX.utils.aoa_to_sheet 将csv结构的二维数组转 sheet
  • XLSX.utils.json_to_sheet 将json格式数据转 sheet
  • XLSX.utils.table_to_sheet 将一个table dom直接转成 sheet,会自动识别colspanrowspan并将其转成对应的单元格合并
  • XLSX.utils.sheet_add_aoa 向现有的工作表中添加一个csv数组
  • XLSX.utils.sheet_add_json 将一个json对象添加到现有的工作表中
const sheetName = 'sheet1';
const aoa = [['姓名','性别','年龄','注册时间'], ['张三','男','18','9/16/21','22'], ['李四','女','22','9/16/21','26'], ['主要信息','','','其它信息']];
const csv = `姓名,性别,年龄,注册时间,\n张三,男,18,9/16/21,22\n李四,女,22,9/16/21,26\n主要信息,,,其它信息,`;
const json = [
    { '姓名': '张三', '年龄': 18, '性别': '男', '注册时间': 44455.62984811343 },
    { '姓名': '李四', '年龄': 22, '性别': '女', '注册时间': 44455.62984811343 },
    { '姓名': '主要信息', '注册时间': '其它信息' },
];

workbook.SheetNames.push(sheetName);
// workbook.Sheets[sheetName] = XLSX.utils.json_to_sheet(json);
// workbook.Sheets[sheetName] = XLSX.utils.aoa_to_sheet(aoa);
// workbook.Sheets[sheetName] = csv2sheet(csv);
workbook.Sheets[sheetName] = XLSX.utils.table_to_sheet(document.getElementsByTagName('table')[0]);