1.背景
管理后台列表经常会有大量的导出功能,每个导出功能都需要单独写个导出接口,无法通用化。常见的导出方案就是使用 poi 或easyExcel直接同步导出一个 excel 文件。 如果一次性导出的数据量非常大,达到几十万甚至几千万级别的数据,往往就会出现以下几种情况:
- 大数据量同步导出时,,页面等待时间长,页面会卡主,用户无法进行其他操作。
- 出现内存溢出的情况。
- 用户浏览器崩溃。
- 数据库压力大,数据库宕机。
2.功能说明
- 通用的异步导出方案,业务侧不需要关注如何导出,如何生成excel文件,只需提供一个分页查询数据列表接口即可。
- 支持无限数据量文件异步导出,将N个小的excel压缩成zip,提供zip文件的下载链接。
- 支持excel自定义合并表头。
- 提供后台管理界面,供用户实时查看自己的导出进度、结果且可以下载自己导出的文件。
- 前端在进行异步导出时需指明表头与之对应的字段、业务侧分页查询数据的接口(springcloud 网关routesId + uri)。
- 为提升文件服务的存储空间利用率,导出的zip、excel文件在文件服务器中只保留30,过后会自动清除,下载链接失效。
3.异步导出时序图
4.表结构
5.使用
- 业务侧提供的分页查询数据接口返回示例 :
/xxx-system/user/page
{
"code": 200,
"success": true,
"data": {
"records": [
{
"id": "1514480641483673601",
"code": "004",
"name": "木鱼",
"realName": "木鱼",
"tenantId": "00000000",
"tenantName": "武汉XXX科技有限公司",
"userTypeName": "WEB",
"roleName": "用户",
"deptName": "后端组",
"postName": "研发",
"sexName": "性别",
"userExt": ""
}
],
"total": 1,
"size": 10,
"current": 1
},
"msg": "操作成功"
}
前端调用导出服务示例:
let param = {
'fileName': '用户列表', //文件名称,随便定义,必填
'fileType': 'xlsx', //文件类型(xlsx或zip),非必填,默认xlsx
'taskName': '木鱼导出的用户列表', //导出任务名称,随便定义,非必填,如果不填就和文件名称一样
'url': '/user/page', //业务侧提供的分页查询数据接口url,必填
'routeId': 'xxx-system', //业务侧注册到网关的路由标识,必填
'bizParam': { // 业务侧提供的分页查询数据接口所需要的参数,非必填
'name': '张三', //业务侧的查询条件,非必填
'size': 100 // excel的容量,非必填,默认1000
},
'tableHead': [{ // Excel表头内容配置, 参考 数据接口提供方的响应参数格式,必填
'key': 'id',
'title': '用户ID'
}, {
'key': 'name',
'title': '用户姓名'
}, {
'key': 'tenantName',
'title': '所属租户'
}],
}
// 向Excel导出服务发起异步导出请求
axios.create({
method: 'post', // excel接口的参数
headers: {
'Content-Type': 'application/json;charset=UTF-8', // excel接口的参数接收方式
'X-Requested-With': 'XMLHttpRequest'
},
url: 'https://dev.xxxx.com/api/xxx-excel/export/async/process',
data: param
})
表头合并方案
场景1:
'tableHead': [{
'key': 'id',
'title': '用户ID'
}, {
'key': 'name',
'title': '用户姓名'
}]
场景2:
'tableHead': [{
'title': '统计',
'children': [{
'key': 'total',
'title': '总计'
}, {
'key': 'avg',
'title': '平均'
}]
}, {
'key': 'name',
'title': '用户姓名'
}]
场景3:
'tableHead': [{
'title': '统计',
'children': [{
'title': '总计',
'children': [{
'key': 'a',
'title': 'A'
}, {
'key': 'b',
'title': 'B'
}]
}, {
'key': 'avg',
'title': '平均'
}]
}, {
'key': 'name',
'title': '用户姓名'
}]