<>序
业务要求系统一次性把业务数据导出来。 系统的为了稳定性考虑,限制数据的导出上限为10000,避免数据库卡死,网络卡死,页面无响应等问题。
实际上系统不支持大数据量导出,给业务带来了很大的麻烦。 本文将针对大数据导出进行方案分析现实现。
<>分析
目前做的系统的架构大致如下。
从框架来看,每一个层之前的交互都有超时间。
1:DB超时时间为60秒。
2:Biz与Service层是通过dubbo的,超时时限为120秒。有包大小的限制 8M。另外还有断路器。
3:nginx的超时为60秒。
以上都是支持大数据的支持的障碍,如果仅仅是为了一个导出功能,对在线系统做这么大动作的调整,风险太大了。
那么有没有更好的方案呢?答案是肯定的
<>使用前端导出excel
<>方案
1:用户点击“导出”按钮
2:ajax调用分页查询,获取第一页数量(包括 总数信息)。
3:利前第2步获取的总数,按分每一页数量为10000计算,计算出需要获取次数N。
4:循环去获取数据。从第一页取到第N页。并把数据汇总。
5:使用js拼组excel ,并摸拟用户下载。
<>优点
1:修改无侵入性,框架与后台都不需要改动。
2:可以废除后台的导出服务的支持,与分页共用一个api,减少后台代码的冗余。
3:更具有可扩展性,前端可以直接调整pagesize来调优化下载性能。
<>实现
; (function(define) { 'use strict'; define(function(require, exports, module) {
function ajaxGet(data) { let returnRs; let url = $context.$config.localDomain +
"qgwsclaim/search"; $.ajax({ type: "GET", async: false, url: url, data: data,
contentType: "application/json", success: function(rs) { returnRs = rs; }, error
: function(rs) { alert('导出出错'); //去除遮罩层 loadingShow(false); } }); return
returnRs; } function tableToExcel(data) { //要导出的json数据 let header = {
"claimCode": "索赔单号", "vin": "车架号", "model": "车型", "vehicleUse": "车辆用途",
"configureCode": "配置代码", "dealerCode": "经销商编号", "dealerName": "经销商名称",
"province": "经销商省份", "claimType": "索赔类型" }; let str = "" // `索赔单号,车架号,车型\n`;
//标头 for (let item in header) { str += `${header[item] + '\t'},`; } str += '\n';
//增加\t为了不让表格显示科学计数法或者其他格式 for (let j = 0; j < data.length; j++) { let jsonData =
data[j]; for (let i = 0; i < jsonData.length; i++) { for (let item in header) {
str+= jsonData[i][item] ? jsonData[i][item].toString().replace(new RegExp(/,/g)
, ",") : ''; str += '\t,'; } str += '\n'; } } //encodeURIComponent解决中文乱码 let uri
= 'data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(str); //通过创建a标签实现
let link = document.createElement("a"); link.href = uri; //对下载的文件命名 link.
download= "索赔单.csv"; document.body.appendChild(link); link.click(); document.
body.removeChild(link); controller.loadingShow(false); } //遮罩层 function
loadingShow(isShow) { /*******loading方法*******/ if (isShow) { $("body").append(
'<div id="loadingMask" class="ui-widget-overlay ui-front" style="z-index:
9998;"></div></div>'); //<div style="background: url(./img/loading.gif)"> } else
{ $("#loadingMask").remove(); } } var controller = { //导出EXCLE exportEvent:
function() { controller.loadingShow(true); let reqData = { token: $context.$api.
auth.getToken(), pageIndex: 1, pageSize: 10, vin: $(document).find(
"input[name='vin']").val().trim(), claimCode: $(document).find(
"input[name='claimCode']").val().trim(), tssNo: $(document).find(
"input[name='tssNo']").val().trim(), projectCode: $(document).find(
"input[name='projectCode']").val().trim(), module: $(document).find(
"select[name='module']").val(), multiPart: $(document).find(
"input[name='partCode']").val().trim(), issueDesc: $(document).find(
"input[name='issueDesc']").val().trim(), }; //第一次获取,pageIndex=1,pageSize=10 let
data= this.ajaxGet(reqData); //获取数量,计算需要去取多少次 let totalAcc = data.records; const
oneceAcc= 10000; //定义一次获取的数量 //计算获取的次数,进一制计算 let requestTime = Math.ceil (
totalAcc/ onceAcc); //结果集定义 let totalData = []; for (let i = 1; i <= requestTime
; i++) { reqData.pageIndex = i; reqData.pageSize = oneceAcc; let d1 = this.
ajaxGet(reqData); //结果填充入结果集 totalData.push(d1.rows); } this.tableToExcel(
totalData); } }; module.exports = controller; }); })(define);
代码如上,已去除业务代码。入口函数exportEvent
<>效果
<>补充
经过业务的体验,这个导出的方式也有挺大的弊端,那就是浏览器的内存占用高,数据过多的时候可能会导致浏览器直接挂掉
建议还是做数量的限制。