JAva springmvcスレッドプールで百万級csvデータを効率的にエクスポートし、フロントエンドに進捗バーを表示

13997 ワード

リーダーは、会社のレポートのエクスポートが遅すぎてまだ進捗バーが表示されていないと言っています.以前使っていたpoi操作excelでxlsをエクスポートし、データベースからデータを照会し、ローカルファイルに書き込み、ローカルファイルのダウンロード効率が低下し、特にデータ量が大きい場合です.だから私はバックグラウンドcmsシステムのエクスポートを最適化して、csvフォーマットをエクスポートして、このフォーマットの下で速度はxlsよりずっと速いです.
**csv xls   **

               csv xls  。         ,   csv        , xls    csv   , xls          。               ,        。
xls     Microsoft excel         。          ,         。
CSV           ,             PC       。    ,          。              。

CSV     ,        ,XLS          EXCEL   

CSV(     )

CSV (*.csv)                            。                 。        ,            。          ,               。

                ,           。    、  、                。          。

//ツールクラスimport java.io.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.sf.utils.core.StringUtil; import com.sf.tuxiaoer.service.SfSQLBaseService; import org.slf4j.Logger; import org.slf4j.LoggerFactory;//csvエクスポートツールクラス/**
  • 機能説明CSVダウンロードツールクラス実装Callableインタフェースcall()方法
  • @author hjli
  • @date 2019/7/13
  • @param
  • @return */public class CsvDownload implements Callable { private static final Logger logger = LoggerFactory.getLogger(XlsDownload.class); private int index; private int defaultNums; private String sql_select; private String[] columns; private SfSQLBaseService sfSQLBaseService; private CountDownLatch latch; private HttpServletRequest request; } public CsvDownload (){ }/**
  • 機能説明
  • @author hjli
  • @date 2019/7/13
  • @param index,//データクエリーページング
  • defaultNums,//デフォルトページ数
  • sql_select,//sqlクエリ文
  • columns,//列名
  • sfSQLBaseService,//jdbctemplate
  • latch,//CountDownLatchタイマ
  • request//HttpServeretRequestリクエスト
  • @return */public CsvDownload(int index,int defaultNums,String sql_select, String[] columns,SfSQLBaseService sfSQLBaseService, CountDownLatch latch,HttpServletRequest request){ this.index = index; this.defaultNums = defaultNums; this.sql_select = sql_select; this.columns = columns; this.sfSQLBaseService = sfSQLBaseService; this.latch = latch; this.request = request; }


  • //csv接尾辞private String getFileName(int rowSize){String dateString=DateTimeUtils.getServer Time("yyyyyyMMddHHmmss");String file Name=dateString+""+rowSize + “.csv”; return fileName; }
    //***機能説明CSVダウンロード*@author hjli*@date 2019/7/13*@param request、*response、//レスポンス*sfSQLBAseService、//jdbctemplate*title、//ヘッダ名*column、//カラム名*sql_select,//sqlクエリー文*sql_count//問合せ総数*@return void*/public void csvDownLoad(HttpServertRequest request,HttpServertResponse response,SfSQLbaseServicesfSQLbaseServices,String title,String column,String sql_select,String sql_count)throws InterruptedException{
        String start_date = "";
        String end_date = "";
        
        //      
        start_date = DateTimeUtils.getServerTime();
        
        //      
        StringBuffer stringBuffer = new StringBuffer(1000);
    
        String[] titles = StringUtil.split(title,",");
        String[] columns = StringUtil.split(column,",");
    
        //                1:   、 2   
        request.getSession().setAttribute("downloadType", 1);
        
        //      
        int rowSize = getSize(sfSQLBaseService, sql_count);
    
        //          
        int defaultNums = 0;
        if(rowSize <= 3000){
            defaultNums = 3000;
        }else if(rowSize > 3000 && rowSize <= 10000){
            defaultNums = 5000;
        }else if(rowSize > 10000 && rowSize <= 50000){
            defaultNums = 10000;
        }else if(rowSize > 50000 && rowSize <= 100000){
            defaultNums = 50000;
        }else{
            defaultNums = 100000;
        }
    
    
       //      
        for(int x = 0; x< titles.length; x++){
            stringBuffer.append(titles[x]);
            if(x < titles.length - 1){
                stringBuffer.append(",");
            }
        }
        //  
        stringBuffer.append("\r
    "); // int sheetNums = (int) getSheetNums(rowSize, defaultNums); // // request.getSession().setAttribute("total_data",rowSize); // request.getSession().setAttribute("total_packet",sheetNums); // ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-call-runner-%d").build(); ExecutorService es = new ThreadPoolExecutor(sheetNums,sheetNums,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue(),namedThreadFactory); // CountDownLatch latch = new CountDownLatch(sheetNums); // Future List resultList = new ArrayList<>(); try { for(int i=0;i future = es.submit (new XlsDownload(i, defaultNums, sql_select, columns, sfSQLBaseService,latch,request)); resultList.add(future); } // , latch.getCount() 0 latch.await(); }catch (Exception e){ e.printStackTrace(); }finally { es.shutdownNow();// // session request.getSession().setAttribute("progress_flag",false); request.getSession().removeAttribute("total_data"); request.getSession().removeAttribute("total_packet"); for(int i=0;i>end:"+end_date); try { // out = response.getOutputStream(); out.write(stringBuffer.toString().getBytes("UTF-8")); }catch (Exception e){ e.printStackTrace(); }finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } }

    @Override public String call() throws Exception {
        String tmpSelect = sql_select + " LIMIT " + index * defaultNums + ", " + defaultNums;
        List dataList = sfSQLBaseService.queryForList(tmpSelect);
        String _status = "";
        StringBuffer sb = new StringBuffer();
    
        for(int i=0;i
    "; request.getSession().setAttribute("thread_"+index,_status); } sb.append("\r
    "); request.getSession().setAttribute("size_"+index,(i+1)); request.getSession().setAttribute("count_"+index,index); request.getSession().setAttribute("packet_"+index,index); } logger.info(" :{} , ID:{}",index,Thread.currentThread().getName()); latch.countDown();// countDown return sb.toString(); }

    前にセッションで多くのデータが格納されています.主にフロントエンドページの進捗バーの表示に使用されます.ここでは進捗バーのリスニングのcontrollerを作成する必要があります.
    //controller import com.sf.tuxiaoer.web.BaseController; import net.sf.json.JSONArray; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; import java.text.DecimalFormat; import java.util.HashMap; import java.util.Map;
    /**
    *           
    * @author hjli
    * @date 2019/7/13
    * @param
    * @return
    */
    @Controller
    public class ProgressBar extends BaseController {
    
       private DecimalFormat df = new DecimalFormat("0.00");
    
        /**
         *           
         * @author hjli
         * @date 2019/7/13
         * @param response
         * @return void
         */
        @RequestMapping("/wash/progress/progressBarStatu")
        public void progressBarStatu(HttpServletResponse response){
            Map dataMap = new HashMap();
            try {
                boolean progress_flag = (boolean)request.getSession().getAttribute("progress_flag");
    
                //     
                boolean showFlag = false;
    
                //    
                if(progress_flag == true){
    
                    //     1:   、 2   
                    int downloadType =  request.getSession().getAttribute("downloadType") != null ? (int)request.getSession().getAttribute("downloadType") : 9;
    
                    //   
                    if(downloadType == 1){
    
                        int total_data =request.getSession().getAttribute("total_data") != null ? (int)request.getSession().getAttribute("total_data") : 0;
    
                        int total_packet = request.getSession().getAttribute("total_packet") != null ? (int)request.getSession().getAttribute("total_packet") : 0;
    
                        //    
                        dataMap.put("total_packet",total_packet);
    
                        //   
                        dataMap.put("total_data",total_data);
    
                        //         
                        if(total_packet > 0){
                            int _count = 0;
                            int _size = 0;
                            StringBuffer stringBuffer = new StringBuffer();
                            for(int i=0;i 0){
    
                            //      
                            showFlag = true;
    
                            //      
                            String status =  (String)request.getSession().getAttribute("status");
                            dataMap.put("status",status);
                            dataMap.put("total_size",total_size);
    
                            //     
                            float progress = (float)size * 100 / total_size;
                            dataMap.put("progress",df.format(progress)+"%");
    
                        }
    
                    }
    
                    //     1:   、 2   
                    dataMap.put("downloadType",downloadType);
                }
    
                //         true   false   
                dataMap.put("showFlag",showFlag);
    
                //      true   false  
                dataMap.put("progress_flag",progress_flag);
    
    
                PrintWriter out = response.getWriter();
                out.print(JSONArray.fromObject(dataMap).toString());
                out.close();
            }catch (Exception e){e.printStackTrace();}
        }
    

    //フロントエンドjsページ展示、パブリックjspページに書くことで、すべてのページで進捗バー//CSSを呼び出すことができます .mydiv { -moz-border-radius: 16px; -webkit-border-radius: 16px; border-radius: 16px; background-color:lavender; text-align: center; z-index:99; width:38%; height: auto; left:38%;/*FF IE7*/ top: 8%;/*FF IE7*/ margin-left:-130px!important;/*FF IE7 */ margin-top:-20px!important;/*FF IE7 */ margin-top:0px; position:fixed!important;/*FF IE7*/ position:absolute;/*IE6*/ } .bbg { background-color: #878b80; width: 100%; height: 100%; left:0; top:0;/*FF IE7*/ filter:alpha(opacity=50);/*IE*/ opacity:0.5;/*FF*/ z-index:1; position:fixed!important;/*FF IE7*/ position:absolute;/*IE6*/ } //html //js var interval_time = 1000; var time = 0; // var task = window.setInterval(function(){ time++; $.ajax({ url:'wash/progress/progressBarStatu', type:'post', success:function(data){ var objs = jQuery.parseJSON(data); if(objs[0].progress_flag == true){ $("#bbg").css({display:'block'}); $("#bpopDiv").css({display:'block'}); var msg = " , ......

    " + " :"+time+"s

    " ; if(objs[0].showFlag == true){ // 1: 、 2 if(objs[0].downloadType == 1){ msg += " :"+objs[0].total_data+"

    " + " :"+objs[0]._count+" / "+objs[0].total_packet+"

    " ; msg += ""+objs[0].stringBuffer+"

    " + " :"+objs[0].progress+""; }else if(objs[0].downloadType == 2){ msg += ""+objs[0].status+"

    " + " :"+objs[0].progress+"" ; } $("#progress_bar").html(msg); } }else{ time = 0; //window.clearInterval(task); $("#progress_bar").html(" , ..."); $("#bbg").css({display:'none'}); $("#bpopDiv").css({display:'none'}); } } }); },interval_time);