逐頁緩存查詢(串行,避免OOM)導出Excel,zip壓縮

ExcelGenerator

import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

public class ExcelGenerator {

    /**
     * @param fieldName     Excel中文題頭
     * @param keys          Excel英文字段名
     * @param fieldData     數據結果集
     * @param out           輸出流
     * @param sheetMaxRow   一頁sheet最大記錄數
     * @param baseSheetName sheet頁基本名稱
     * @throws IOException 
     */
    public static void exportExcel(List<String> fieldName, List<String> keys, List<Map<String, Object>> fieldData, OutputStream out,int sheetMaxRow,String baseSheetName) throws IOException {
        // 在內存中緩存100行數據(100測試最佳)
        SXSSFWorkbook workBook = new SXSSFWorkbook(100);
        // 臨時文件進行壓縮,建議不要true,否則會影響導出時間
        workBook.setCompressTempFiles(false);

        int rows = fieldData.size();
        int sheetNum = 0;
        if (rows != 0 && rows % sheetMaxRow == 0) {
            sheetNum = rows / sheetMaxRow;
        } else {
            sheetNum = rows / sheetMaxRow + 1;
        }

        //start 遍歷Excel中的所有頁
        for (int i = 1; i <= sheetNum; i++) {
            Sheet sheet = workBook.createSheet(baseSheetName + " " + i);
            //在第0行創建表頭名行
            Row headRow = sheet.createRow(0);
            //start 遍歷標題名數組,並將其值放置到Excel第一行
            for (int j = 0; j < fieldName.size(); j++) {
                Cell cell = headRow.createCell(j);
                //添加樣式
                cell.setCellType(HSSFCell.CELL_TYPE_STRING);
                //添加樣式
                //設置所有單元格的寬度
                sheet.setColumnWidth(j, 6000);
                //創建樣式(使用工作本的對象創建)
                CellStyle cellStyle = getCellStyle(workBook);

                if(fieldName.get(j) != null){
                    cell.setCellStyle(cellStyle);
                    cell.setCellValue(fieldName.get(j));
                }else{
                    cell.setCellStyle(cellStyle);
                    cell.setCellValue("-");
                }
            }
            //end 遍歷標題名數組,並將其值放置到Excel第一行

            //start 遍歷sheet中的每行記錄(創建第i個sheet頁中的DataList,其行數爲K)
            int kLimit = rows-(i-1)*sheetMaxRow < sheetMaxRow ? rows-(i-1)*sheetMaxRow : sheetMaxRow;
            for (int k = 0; k < kLimit; k++) {
                Row row = sheet.createRow(k + 1);
                //將數據內容放入excel單元格
                Map<String, Object> rowList =  fieldData.get((i - 1) * sheetMaxRow + k);

                //start 遍歷每條記錄中的數據開始
                for (int n = 0; n < keys.size(); n++) {
                    Cell cell = row.createCell( n);

                    if(rowList.get(keys.get(n)) != null){
                        Object cellData = rowList.get(keys.get(n));
                        if (cellData instanceof Date) {
                           Date date = (Date) cellData;
                           SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                           cellData = sdf.format(date);
                        } else {// 其它數據類型都當作字符串簡單處理
                           cellData = cellData.toString();
                        }
                        cell.setCellValue(cellData.toString());
                    }else{
                        cell.setCellValue("");
                    }
                }//end 遍歷每條記錄中的數據結束
            }//end 遍歷sheet中的每行記錄結束
             ((SXSSFSheet)sheet).flushRows();
        }//end 遍歷Excel中的所有頁結束
        workBook.write(out);
        out.close();
    }


    /**
     * @param fieldName     Excel中文題頭
     * @param keys          Excel英文字段名
     * @param fieldData     數據結果集
     * @param sheetMaxRow   一頁sheet最大記錄數
     * @param baseSheetName sheet頁基本名稱
     * @throws IOException 
     */
    public static void flushSheet(List<String> fieldName, List<String> keys,SXSSFWorkbook workBook,
            List<Map<String, Object>> fieldData, String baseSheetName) throws IOException {
            Sheet sheet = workBook.createSheet(baseSheetName);
            //在第0行創建表頭名行
            Row headRow = sheet.createRow(0);
            //start 遍歷標題名數組,並將其值放置到Excel第一行
            for (int j = 0; j < fieldName.size(); j++) {
                Cell cell = headRow.createCell(j);
                //添加樣式
                cell.setCellType(HSSFCell.CELL_TYPE_STRING);
                //添加樣式
                //設置所有單元格的寬度
                sheet.setColumnWidth(j, 6000);
                //創建樣式(使用工作本的對象創建)
                CellStyle cellStyle = getCellStyle(workBook);

                if(fieldName.get(j) != null){
                    cell.setCellStyle(cellStyle);
                    cell.setCellValue(fieldName.get(j));
                }else{
                    cell.setCellStyle(cellStyle);
                    cell.setCellValue("-");
                }
            }
            //end 遍歷標題名數組,並將其值放置到Excel第一行

            //start 遍歷sheet中的每行記錄(創建第i個sheet頁中的DataList,其行數爲K)
            for (int k = 0; k < fieldData.size(); k++) {
                Row row = sheet.createRow(k + 1);
                //start 遍歷每條記錄中的數據開始
                for (int n = 0; n < keys.size(); n++) {
                    Cell cell = row.createCell( n);
                    if(fieldData.get(k).get(keys.get(n)) != null){
                        Object cellData = fieldData.get(k).get(keys.get(n));
                        if (cellData instanceof Date) {
                           Date date = (Date) cellData;
                           SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                           cellData = sdf.format(date);
                        } else {// 其它數據類型都當作字符串簡單處理
                           cellData = cellData.toString();
                        }
                        cell.setCellValue(cellData.toString());
                    }else{
                        cell.setCellValue("");
                    }
                }//end 遍歷每條記錄中的數據結束
            }//end 遍歷sheet中的每行記錄結束
             ((SXSSFSheet)sheet).flushRows();
    }

    /**
     * 設置Cell樣式
     * @param workBook
     * @return
     */
    private static CellStyle getCellStyle(SXSSFWorkbook workBook) {
        CellStyle style = workBook.createCellStyle();
        style.setAlignment(CellStyle.ALIGN_CENTER);
        style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
        return style;
    }
}

調用

    @Value("${poi.excel.maxSheet}")
    private String excelMaxSheets;

    @Value("${poi.excel.sheet.maxRow}")
    private String sheetMaxRows;


    public void export(HttpServletRequest request, HttpServletResponse response, EventParam eventParam) {
        logger.info("###############開始導出告警Excel文件###############");
        Long t0 = System.currentTimeMillis();
        Map<String,Object> viewParam = new HashMap<String,Object>();

        logger.info("###開始檢索控制檯條件");
        viewParam.put("viewId", eventParam.getSelectCountViewId() != null ? eventParam.getSelectCountViewId():
                                eventParam.getViewId() != null ? eventParam.getViewId() :1L);
        List<MonViewInfo> viewInfoList = monViewInfoDao.selectByCondition(viewParam);

        List<String> fieldNameList = new ArrayList<String>();
        List<String> keyList = new ArrayList<String>();
        for(int i = 0; i < viewInfoList.size(); i++) {//-2是去掉“持續時間”和“操作”列
            if(!viewInfoList.get(i).getColName().equals("INSTANTTIME") && !viewInfoList.get(i).getColName().toLowerCase().equals("operate")){
                fieldNameList.add(viewInfoList.get(i).getColTitle());
                keyList.add(viewInfoList.get(i).getColName());
            }
        }
        logger.info("###檢索控制檯條件結束");
        //補充確認和關閉相關字段start
        List<MonEventModel> eventModelList = monEventModelDao.selectList(null, null);
        Map<String,String> eventModelMap = new HashMap<String,String>();
        for(int i = 0; i < eventModelList.size(); i++) {
            eventModelMap.put(eventModelList.get(i).getEventCode().toUpperCase(), eventModelList.get(i).getEventName());
        }

        if(eventParam.getIsHistory() != null && eventParam.getIsHistory()) {
            keyList.add("ACKUID");
            keyList.add("ACKINFO");
            keyList.add("ACKTIME");
            keyList.add("CLOSEUID");
            keyList.add("CLOSEINFO");
            keyList.add("CLOSETIME");

            fieldNameList.add(eventModelMap.get("ACKUID"));
            fieldNameList.add(eventModelMap.get("ACKINFO"));
            fieldNameList.add(eventModelMap.get("ACKTIME"));
            fieldNameList.add(eventModelMap.get("CLOSEUID"));
            fieldNameList.add(eventModelMap.get("CLOSEINFO"));
            fieldNameList.add(eventModelMap.get("CLOSETIME"));
        } else {
            keyList.add("ACKUID");
            keyList.add("ACKINFO");
            keyList.add("ACKTIME");
            fieldNameList.add(eventModelMap.get("ACKUID"));
            fieldNameList.add(eventModelMap.get("ACKINFO"));
            fieldNameList.add(eventModelMap.get("ACKTIME"));
        }

        Map<String,Object> paramMap = super.handleParam(eventParam);
        logger.info("##########告警列表查詢條件:\n"+paramMap);

        Long rows = eventParam.getTotalRows();
//      Long rows = monEapEventShowDao.queryCount(mapParam);
        logger.info("##########總共有"+rows+"條要下載的數據");

        //若數據量小於10W,一個Excel下載,若是大於10W,分多個Excel封裝,並壓縮成Zip文件
        Long excelMaxSheet = Long.parseLong(excelMaxSheets);
        Long sheetMaxRow = Long.parseLong(sheetMaxRows);
        Long excelMaxRow = excelMaxSheet * sheetMaxRow;

        Long realSheetNum = 0L;
        if (rows != 0 && rows % sheetMaxRow == 0) {
            realSheetNum = rows / sheetMaxRow;
        } else {
            realSheetNum = rows / sheetMaxRow + 1;
        }


        logger.info("##########每個Excel最多有"+excelMaxSheet+"個sheet");
        logger.info("##########每個分頁SQL查詢"+sheetMaxRow+"條數據,即每個sheet中的數據條數");
        logger.info("##########每個Excel最多有"+excelMaxRow+"條數據");


        if(rows <= excelMaxRow) {   //如果數據小於等於一個Excel的最大容量,則導出Excel
            // 在內存中緩存100行數據(100測試最佳)
            SXSSFWorkbook workBook = new SXSSFWorkbook(100);
            // 臨時文件進行壓縮,建議不要true,否則會影響導出時間
            workBook.setCompressTempFiles(false);

            for(Long i = 1L; i <= realSheetNum; i++){
                List<Map<String,Object>> subEvents = monEapEventShowDao.queryEventPageWithoutCount(i, sheetMaxRow, paramMap);

                logger.info("########## " + "sheet "  + realSheetNum + "結果集豐富start ");
                //豐富集字段
                super.richEvent(subEvents, eventParam);
                logger.info("########## " + "sheet "  + realSheetNum + "結果集豐富end ");

                try {
                   logger.info("########## 將sheet "  + realSheetNum + "結果集寫入到Excel文件start ");
                   ExcelGenerator.flushSheet(fieldNameList, keyList, workBook,subEvents,"Event List "+i);
                   logger.info("########## 將sheet "  + realSheetNum + "結果集寫入到Excel文件end ");
               } catch (IOException e) {
                   logger.error(e.getMessage(), e);
                   logger.info("將數據寫入到Excel文件中出錯");
               }

            }
            try {
                workBook.write(response.getOutputStream());
                response.getOutputStream().close();
            } catch (IOException e) {
                logger.info("單個Excel下載時數據流導出至頁面異常");
                logger.error(e.getMessage(), e);
            }
        } else {                    //如果數據大於一個Excel的最大容量,則導出zip壓縮文件,其中包含多個Excel
            logger.error("The event data is too big to export!!");
            logger.info("##########文件將以.zip壓縮文件形式下載");

            //zip文件中的Excel文件數
            Long excelNumber = 0L;
            //Excel文件中的sheet頁數量
            Long sheetNumber = 0L;

            if (rows != 0 && rows % excelMaxRow == 0) {
                excelNumber = rows / excelMaxRow;
            } else {
                excelNumber = rows / excelMaxRow + 1;
            }

            logger.info("##########zip壓縮文件中的excel數量 : " + excelNumber);

            if (rows != 0 && rows % sheetMaxRow == 0) {
                sheetNumber = rows / sheetMaxRow;
            } else {
                sheetNumber = rows / sheetMaxRow + 1;
            }

            logger.info("##########所有Excel中sheet數量總和 : " + sheetNumber);

            String excelDate = DateUtil.date2Str(new Date(), DateUtil.YYYYMMddHHmmss);
            String excelBaseName = "Event-"+ excelDate + "-*.xlsx";

            //遍歷zip壓縮文件中的每個Excel文件
            for(Long j = 0L; j < excelNumber; j++) {
                // 在內存中緩存100行數據(100測試最佳)
                SXSSFWorkbook workBook = new SXSSFWorkbook(100);
                // 臨時文件進行壓縮,建議不要true,否則會影響導出時間
                workBook.setCompressTempFiles(false);
                String excelName = "Event-" + excelDate + "-"+ (j+1) + ".xlsx";
                File excelFile = new File(excelName);
                FileOutputStream out = null;
                try {
                    out = new FileOutputStream(excelFile);
                } catch (FileNotFoundException e) {
                    logger.info("創建內存Excel文件時出錯");
                }

                Long thisExcelRealSheetNum = excelMaxSheet;
                if(j == excelNumber-1) { //最後一個Excel的sheet多少不定
                    thisExcelRealSheetNum = sheetNumber - (excelNumber-1) * excelMaxSheet; //總sheet數量 - (excel數量-1)*excelMaxSheet
                }

                for(Long i = 1L; i <= thisExcelRealSheetNum; i++) {
                    Long sheetNum = j*excelMaxSheet+i;
                    List<Map<String,Object>> subEvents = monEapEventShowDao.queryEventPageWithoutCount(sheetNum, sheetMaxRow, paramMap);
                    logger.info("##########總sheet數:"+ sheetNumber + " , sheet "  + sheetNum + "結果集豐富start ");
                    //豐富集字段
                    super.richEvent(subEvents, eventParam);
                    logger.info("##########總sheet數:"+ sheetNumber + " , sheet "  + sheetNum + "結果集豐富end ");
                    try {
                       logger.info("##########總sheet數:"+ sheetNumber + " , 將sheet "  + sheetNum + "結果集寫入到" + excelName + " start ");
                       ExcelGenerator.flushSheet(fieldNameList, keyList, workBook,subEvents,"Event List "+ (j*excelMaxSheet+i));
                      logger.info("##########總sheet數:"+ sheetNumber + " , 將sheet "  + sheetNum + "結果集寫入到" + excelName + " end ");
                   } catch (IOException e) {
                      logger.error(e.getMessage(), e);
                       logger.info("將數據寫入到Excel文件中出錯");
                   }
                }

                try {
                    workBook.write(out);
                    out.close();
                    logger.info("@@@@@@@@@@" + excelName +"文件寫入數據完畢");
                } catch (IOException e) {
                    logger.error(e.getMessage(), e);
                    logger.info(excelName+ "緩存至本地時異常");
                }
            }

            logger.info("================數據查詢結束,開始生成.zip壓縮文件,敬請期待......");
            List<File> excelFileList = FileUtil.getFiles("./", excelBaseName);
            try {
                File zip = new File("Event-"+ excelDate + ".zip");// 壓縮文件
                FileUtil.zipFiles(excelFileList, zip,response.getOutputStream());
                FileUtil.deleteFile(zip);
                FileUtil.deleteFile(excelFileList);
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
        logger.info("###############導出告警Excel文件結束###############耗時" + (System.currentTimeMillis() - t0) + "ms");
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章