解决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();
       }
   }
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值