前言:
excel的导出是开发常见的功能,但是excel都是谁导出呢?
一般情况excel都是由后端导出生成blob格式返给前端,前端进行下载,当然前端也可以自己利用数据进行excel导出,前端导出需要考虑兼容性问题,这篇文章将介绍前端导出和后端导出的一些插件使用,当然对于数据量大的处理也需要提一下,所以文章还会讲解数据量大的问题
node端导出
1.node-xlsx
官网: node-xlsx - npm
根据官网,可以看到这个插件既可以生成excel,也可以对excel进行解析,这里主要讲解生成excel
(1)下载插件
npm install node-xlsx
(2)使用
引入
const xlsx = require('node-xlsx')
创建buffer
buffer = xlsx.build([{name: 'excel表名', data: 'excel数据'}], sheetOptions);
参数说明:
sheetOptions :为表格样式
const sheetOptions = {'!cols': [{wch: 6}, {wch: 7}]};
data的数据格式为[{},{}] 可以传入多个对象生成多个小excel ,每个对象的数据格式
{
name: 'excel小表名'
data: [['一行一列数据', '一行二列数据'],
['二行一列数据', '二行二列数据']]
}
除了直接写成buffer,也可以写成文件保存在后端,给前端excel的地址进行下载
写成文件的坏处: 会存储,占用内存,一定量要进行清除
const fs = require('fs')
fs.writeFile("文件名称.xlsx", buffer, function (err) {
if (err) {
console.log('写入失败‘);
} else {
console.log("写入成功!");
}
});
使用案例:
egg的写的导出excel的service类
const xlsx = require('node-xlsx')
const Service = require('egg').Service;
class exportFileService extends Service {
constructor(prop) {
super(prop);
}
// const column = [
// { header: '姓名', key: 'name', width: 30 },
// { header: '年龄', key: 'age', width: 30 },
// { header: '生日', key: 'birthday', width: 30 },
// ];
// data = [{name:xx, age:xx, birthday:xx}]
toExport(column, data, filename = '导出') {
return new Promise((reslove, reject) => {
const { ctx } = this;
const colArr = []
colArr.push([...column.map(col => col.header)])
//xlsx数据
data.forEach(item => {
colArr.push([...column.map(col => item[col.key])])
})
console.log(column, data, 'dada')
const buffer = xlsx.build([{ name: '导出sheet1', data: colArr }], { '!cols': [{ wch: 20 }, { wch: 20 }, { wch: 20 }, { wch: 20 }, { wch: 20 }, { wch: 20 },] });
reslove({buffer})
})
}
}
module.exports = exportFileService;
egg端调用servece反给前端
async downLoad() {
const { ctx, service } = this;
const { id, type } = ctx.request.body;
let result = [],
// node段请求后端接口返回数据
const res = await ctx.service.getList.list({ id, type})
if (res.status === 200 && res.data.length) {
result = res.data;
}
const header = [
{ header: '姓名', key: 'name', width: 30 },
{ header: '年龄', key: 'age', width: 30 },
{ header: '生日', key: 'birthday', width: 30 },
];
// node端进行转excel
try {
let res = await service.exportExcel.toExport(header, result);
ctx.set('Content-disposition', `attachment;filename=1.xlsx`);
// 返回文件buffer
ctx.body = res.buffer;
} catch(err) {
ctx.status = 500
ctx.body = err
}
}
前端调用下载
down(data) {
axios({
method: 'post',
url: '/list/downLoad',
responseType: 'blob',
headers: {
'Content-Type': 'application/json',
},
data,
}).then(res => {
if (res.status === 200) {
let name = data.type === 'all' ? '全部列表' : '失败列表'
this.saveFile(res.data, `${name}${data.id}`);
)
}
}).catch(error => {
});
}
saveFile(file, filename) {
const ieKit = /(?:ms|\()(ie)\s([\w\.]+)|trident|(edge|edgios|edga|edg)/i.test(window.navigator.userAgent);
const blobData = new Blob([ file ], { type: 'application/vnd.ms-excel' });
if (ieKit) {
navigator.msSaveBlob && navigator.msSaveBlob(blobData, filename + '.xlsx');
} else {
const objectURL = window.URL.createObjectURL(blobData);
const save_link = document.createElement('a');
const event = document.createEvent('MouseEvents');
save_link.href = objectURL;
save_link.download = filename + '.xlsx';
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
save_link.dispatchEvent(event);
window.URL.revokeObjectURL(objectURL);
}
}
2.exceljs
官网:https://github.com/exceljs/exceljs/blob/master/README_zh.md
(1) 下载
npm install exceljs
(2) 使用
直接看官网吧,因为官网写的很详细,如果去创建一个excel,怎么向一个表增加数据,api有些多,所以自己看吧,最后也是可以返回一个buffer给前端,前端下载;
其实有了buffer,再利用fs写一个文件给前端文件地址也是ok的
前端直接导出excel
1.js-export-excel
官网:GitHub - jiengsad/js-export-excel: json导出excel(纯js 支持中文) ES6 module
是一个纯js进行导出的,所以适用于vue和react,因此要注意其兼容性,ie 10+
// 直接导出文件
const ExportJsonExcel = require("js-export-excel");
var option = {};
option.fileName = "excel";
option.datas = [
{
sheetData: [
{ one: "一行一列", two: "一行二列" },
{ one: "二行一列", two: "二行二列" },
],
sheetName: "sheet",
sheetFilter: ["two", "one"],
sheetHeader: ["第一列", "第二列"],
columnWidths: [20, 20],
},
{
sheetData: [
{ one: "一行一列", two: "一行二列" },
{ one: "二行一列", two: "二行二列" },
],
},
];
var toExcel = new ExportJsonExcel(option); //new
toExcel.saveExcel(); //保存
数据量大excel导出处理
在本个案例中,node做的处理是-》循环调用后端接口,拿到所有数据开始写入excel,最后将二进制流返给前端
当数据量过大,比如3w条数据,会发现node端一次性写入在本地导出下载都非常完美,但是发布到线上之后,数据量过大,会直接服务器错误,502;
前端也可以导出excel,如上所诉;但是前端导出会有兼容性问题(如果是测试部署后前端没有问题,前端去导出大数据是很完美的,测试下来速度很快),刚好这个项目部署到线上平台前端有兼容性,因此最后还是服务端进行处理;
由于问题是只有部署到线上才有问题,所以第一时间想到了是否是配置下载的大小被限制了,线上环境配置的容器内存很小,因此在写入大数量的时候,特别容易崩溃
性能消耗比较大的地方,一个是API数据写入excel库对象的时候,一个是写完以后把数据转为二进制流;
为了处理这个问题,最后变更node端;
(1)将写入的文件分批次(2000条,2000条)写入存在一个excel文件,不再一次性返多条数据的二进制流,而是变更为返回文件地址;
(2) 多个请求放入队列,当一个excel文件全部写入成功,才开始进行写入下一个请求的excel
(3)node端的Excel文件存储起来是没有用的且占用存储空间,因此还需要做一个定时清理的工作;
其实,这并不是最好的处理方案,这样处理下来
优点: 数据量的时候,不会再导致服务器崩溃,解决了服务器崩溃问题
缺点:对于前端来说,发起一个请求,得到了这条数据的一个状态,需要隔断时间轮询服务器是否已经下载完成
这里暂时不放源码了,这个方案并不是最优方案,目前发现可以进行创建流,分段传输,但是因为对node端还不是特别熟悉,所以需要再继续研究,希望有好的建议的也一起讨论