解決POI百萬數據量的導出與導入

產生原因:

因爲在poi的用戶模式中,都是使用批量處理數據,也就是說導入時,將文檔所有數據先讀取存放在內存中,待完全讀取完畢之後再進行導入操作(期間內存被佔用的資源不可釋放,垃圾回收機制也是無法進行資源釋放的),導出亦如此。

導致後果:

內存不斷被佔用,如果在數據導出/導入的過程中,內存資源被完全佔用,則會出現內存溢出,嚴重則可能使項目崩潰

解決方案:

導出:

  1. 減少對象產生(不創建額外的樣式和字體)
  2. 用SXSSFWorkbook進行poi工作簿的創建(後續操作和普通 XSSFWorkbook使用相同),則會將閾值溢出的數據先保存到磁盤,待所有數據處理完成之後,一同合併到下載流中,進行數據導出並下載。
		//設置時間,用作報表名字和員工查詢條件
        String month = "2020-1";

        //獲取數據 (根據自己情況修改)
        List<EmployeeReportResult> list =  userCompanyPersonalService.findByRepost(companyId,month);

        //構造excel
        //創建工作簿
        SXSSFWorkbook workbook = new SXSSFWorkbook(100);
        //創建sheet
        Sheet sheet = workbook.createSheet();

        //構造標題 (根據自己情況修改)
        String[] titles = {"編號", "姓名", "手機","最高學歷", "國家地區", "護照號", "籍貫", "生日", "屬相","入職時間","離職類型","離職原因","離職時間"};
        //寫入標題
        //創建行
        Row row = sheet.createRow(0);
        int cellNum = 0;
        for (String title : titles) {
            //創建單元格
            Cell cell = row.createCell(cellNum);
            cell.setCellValue(title);
            cellNum ++;
        }

        //寫入內容  (根據自己情況修改)
        AtomicInteger headersAi = new AtomicInteger();

        for(int i = 0; i<100000; i ++){
            Cell cell = null;
            for (EmployeeReportResult result : list) {
                //創建單元格
                Row dataRow = sheet.createRow(headersAi.getAndIncrement());
                //編號
                cell = dataRow.createCell(0);
                cell.setCellValue(result.getUserId());
                //姓名
                cell = dataRow.createCell(1);
                cell.setCellValue(result.getUsername());
                //手機
                cell = dataRow.createCell(2);
                cell.setCellValue(result.getMobile());
                //最高學歷
                cell = dataRow.createCell(3);
                cell.setCellValue(result.getTheHighestDegreeOfEducation());
                //國家地區
                cell = dataRow.createCell(4);
                cell.setCellValue(result.getNationalArea());
                //護照號
                cell = dataRow.createCell(5);
                cell.setCellValue(result.getPassportNo());
                //籍貫
                cell = dataRow.createCell(6);
                cell.setCellValue(result.getNativePlace());
                //生日
                cell = dataRow.createCell(7);
                cell.setCellValue(result.getBirthday());
                //屬相
                cell = dataRow.createCell(8);
                cell.setCellValue(result.getZodiac());
                //入職時間
                cell = dataRow.createCell(9);
                cell.setCellValue(result.getTimeOfEntry());
                //離職類型
                cell = dataRow.createCell(10);
                cell.setCellValue(result.getTypeOfTurnover());
                //離職原因
                cell = dataRow.createCell(11);
                cell.setCellValue(result.getReasonsForLeaving());
                //離職時間
                cell = dataRow.createCell(12);
                cell.setCellValue(result.getResignationTime());
            }

        }

         //通過輸出流進行文件下載
         String name = month+"_人事報表.xlsx";
        ServletOutputStream out = response.getOutputStream();
        //指定格式
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("content-Disposition", "attachment;filename=report.xlsx");

        workbook.write(out);

        out.flush();
        out.close();

導入:

  1. 使用Sax事件處理器,每獲取一行文檔,則將數據讀取到,觸發處理器,將已讀的數據從內存中刪除。
  2. 自定義Sax的解析處理器
//自定義Sheet基於Sax的解析處理器
public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {
    //封裝實體對象
    private PoiEntity entity;
    /**
     * 解析行開始
     */
    @Override
    public void startRow(int rowNum) {
        if (rowNum >0 ) {
            entity = new PoiEntity();
       }
   }

    /**
     * 解析每一個單元格
     */
    @Override
    public void cell(String cellReference, String formattedValue, XSSFComment comment)
{
        if(entity != null) {
          //判斷單元格名稱,Excel上面的A.B.C.....標題
            switch (cellReference.substring(0, 1)) {
                case "A":
                    entity.setId(formattedValue);
                    break;
                case "B":
                    entity.setBreast(formattedValue);
                    break;
                case "C":
                    entity.setAdipocytes(formattedValue);
                    break;
                case "D":
                    entity.setNegative(formattedValue);
                    break;
                case "E":
                    entity.setStaining(formattedValue);
                    break;
                case "F":
                    entity.setSupportive(formattedValue);
                    break;
                default:
                    break;
           }
       }
   }

    /**
     * 解析行結束
     */
    public void endRow(int rowNum) {
        System.out.println(entity);
   }

    //處理頭尾
    public void headerFooter(String text, boolean isHeader, String tagName) {
   }
}
  1. 自定義Excel解析器
/**
 * 自定義Excel解析器
 */
public class ExcelParser {
  public void parse(String path) throws Exception {
    //1.根據Excel獲取OPCPackage對象
    OPCPackage pkg = OPCPackage.open(path, PackageAccess.READ);
    try {
      //2.創建XSSFReader對象
      XSSFReader reader = new XSSFReader(pkg);
      //3.獲取SharedStringsTable對象
      SharedStringsTable sst = reader.getSharedStringsTable();
      //4.獲取StylesTable對象
      StylesTable styles = reader.getStylesTable();
      //5.創建Sax的XmlReader對象
      XMLReader parser = XMLReaderFactory.createXMLReader();
      //6.設置處理器
      parser.setContentHandler(new XSSFSheetXMLHandler(styles, sst, new
              SheetHandler(), false));
      XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator)
              reader.getSheetsData();
      //7.逐行讀取
      while (sheets.hasNext()) {
        InputStream sheetstream = sheets.next();
        InputSource sheetSource = new InputSource(sheetstream);
        try {
          parser.parse(sheetSource);
        } finally {
          sheetstream.close();
        }
      }
    } finally {
      pkg.close();
       }
   }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章