分享

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>

excel导出案例

留言