sheetjs/js-xlsx前端数据导出为excel文件
在日常项目开发过程中经常会用到将数据导出excel,大部分项目是在后端实现,但是项目如果导出使用人数过多,后端生成excel会占用很大服务器资源,有时候数据量大可能会造成后端服务直接奔溃或者超时,鉴于以上问题和导出数据量大的需求,结合实际考虑将数据导出放在前端实现,数据量少可以一次性导出,但是如果数据量大的情况下,如几w条,甚至几十万条,如果一次性返回数据,后端和前端都有可能奔溃,所以采用分批查询后端,然后再合并数据导出的方案,开源社区有个插件SheetJS/js-xlsx就能实现前端的数据导出excel文件,以下大概为代码实现原理:使用插件,向后端查询有多少数据量需要导出,获取数据总数,再通过前端分批向后端查询,并合并数据,最后导出excel文件,代码如下:
案例实现:vue+sheetjs
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <link rel="stylesheet" href="/export/css/bootstrap.min.css"> <link href="/export/css/bootstrap-datetimepicker.min.css" rel="stylesheet"> <script src="/export/js/vue.min.js"></script> <script src="/export/js/vue-resource.min.js"></script> <script src="/export/js/jquery-2.0.3.min.js"></script> <script src="/export/js/bootstrap.min.js"></script> <!-- SheetJS js-xlsx library --> <script src="/export/js/shim.min.js"></script> <script src="/export/js/xlsx.full.min.js"></script> <!-- FileSaver.js is the library of choice for Chrome --> <script src="/export/js/Blob.js"></script> <script src="/export/js/FileSaver.js"></script> <!-- FileSaver doesn't work in older IE and newer Safari; Downloadify is the flash fallback --> <script src="/export/js/swfobject.js"></script> <script src="/export/js/downloadify.min.js"></script> <script src="/export/js/moment-with-locales.js"></script> <script src="/export/js/bootstrap-datetimepicker.min.js"></script> <!-- 以上js插件均可以网上下载最新版本,本文末提供案例下载 --> </head> <body> <div class="alert alert-success" role="alert">+发货清单下载</div> <div class="form-group"> <div class='col-xs-4'> <div class="form-group"> <label>开始时间:</label> <!--指定 date标记--> <div class='input-group date' id='datetimepicker1'> <input type='text' class="form-control" id='date_start'/> <span class="input-group-addon"> <span class="glyphicon glyphicon-calendar"></span> </span> </div> </div> </div> <div class='col-xs-4'> <div class="form-group"> <label>结束时间:</label> <!--指定 date标记--> <div class='input-group date' id='datetimepicker2'> <input type='text' class="form-control" id='date_end'/> <span class="input-group-addon"> <span class="glyphicon glyphicon-calendar"></span> </span> </div> </div> </div> </div> <div class="row" style="width: 100%"></div> <div class="form-group"> <div id="app" class="col-xs-3"> <button ref="export_btn" class="btn btn-primary" style="float:none;margin-left:0;" type="button" @click="exportExcel"> <span v-show="export_percentage==-1">下载报表</span> <span v-show="export_percentage!=-1">导出中:{{export_percentage}}%</span> </button> <div v-show="export_percentage!=-1" class="progress" style="margin:10px;"> <div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" :style="{width:export_percentage+'%'}"></div> </div> </div> </div> </body> <script> var app=new Vue({ el: '#app', data: function(){ return { export_percentage:-1, count:10000, } }, methods: { exportExcel: function(){ var self=this; //var request_times=Math.ceil(this.count/100); //计算分几次请求 var aoa=[]; var funcs=[]; //Promise.all要用到的参数, 存放每次请求的Promise对象 var complete_count=0; //成功请求次数 var date_start=$('#date_start').val(); var date_end=$('#date_end').val(); self.$http.get( '/export/v2_excel_xf_order.php',//后端获取数据量以及将要导出的excel表头(第一行) { params: {iscount:1,date_start:date_start,date_end:date_end} } ).then(function(response){ if (response.body.code==200) { console.log(response.body.pagination.count); //设置表头数组 for(var i=0; i<response.body.data.length; i++){ var item=[]; var jdata=response.body.data[i]; for(var p in jdata){//遍历json对象的每个key/value对,p为key item.push(jdata[p]); } aoa.push(item); } var request_times=Math.ceil(response.body.pagination.count/1000); //计算分几次请求 this.export_percentage=0; //设置当前进度百分比为0 for (var i=1; i<=request_times; i++) { // 循环请求次数,构造请求的Promise对象并插入funcs数组 var func=new Promise(function(resolve, reject){ //定义Promise并处理请求逻辑 self.$http.get( '/export/v2_excel_xf_order.php',//分批获取数据 { params: {page:i,date_start:date_start,date_end:date_end} } ).then(function(response){ if (response.body.code==200) { var data=[]; for(var i=0; i<response.body.data.length; i++){ //将后台返回中需要的字段取出 var item=[]; var jdata=response.body.data[i]; for(var p in jdata){//遍历json对象的每个key/value对,p为key item.push(jdata[p]); } data.push(item); } complete_count++; //成功请求次数+1 self.export_percentage=Math.floor(100*complete_count/request_times); //设置当前进度百分比 resolve(data); } else { reject(); } }); }); funcs.push(func); } Promise.all(funcs).then(function(values){ //使用Promise.all调用funcs里面的函数,并合并数据,最后给js-xlsx生成Excel //将数据合并 for (var i=0; i<values.length; i++) { for (var j=0; j<values[i].length; j++) { aoa.push(values[i][j]); } } var ws = XLSX.utils.aoa_to_sheet(aoa); var wb=XLSX.utils.book_new(); wb.SheetNames.push('Sheet1'); wb.Sheets['Sheet1'] = ws; var wopts = { bookType:'xlsx', bookSST:false, type:'array' }; var wbout = XLSX.write(wb,wopts); saveAs(new Blob([wbout],{type:"application/octet-stream"}), date_start+'至'+date_end+'新风空净产品发货清单.xlsx'); self.export_percentage=-1; }); } else { return alert(response.body.msg); } }); } } }); $(function () { var picker1 = $('#datetimepicker1').datetimepicker({ format: 'YYYY-MM-DD', locale: moment.locale('zh-cn'), //minDate: '2016-7-1' defaultDate:getQueryVariable('StartDate') }); var picker2 = $('#datetimepicker2').datetimepicker({ format: 'YYYY-MM-DD', locale: moment.locale('zh-cn'), defaultDate:getQueryVariable('OverDate') }); //动态设置最小值 picker1.on('dp.change', function (e) { picker2.data('DateTimePicker').minDate(e.date); }); //动态设置最大值 picker2.on('dp.change', function (e) { picker1.data('DateTimePicker').maxDate(e.date); }); }); function getQueryVariable(variable) { var query = window.location.search.substring(1); var vars = query.split("&"); for (var i=0;i<vars.length;i++) { var pair = vars[i].split("="); if(pair[0] == variable){return pair[1];} } return(false); } </script>