poi读取excel报错:Zip bomb detected! The file would exceed the max

功能描述

就是一个维护功能,支持通过模板将数据导出,修改后导入回系统进行修改;

复现过程

从系统中导出后直接导入,不存在修改,直接报错了

Caused by: java.io.IOException: Zip bomb detected! The file would exceed the max. ratio of compressed file size to the size of the expanded data.
This may indicate that the file is used to inflate memory usage and thus could pose a security risk.
You can adjust this limit via ZipSecureFile.setMinInflateRatio() if you need to work with files which exceed this limit.
Uncompressed size: 103140, Raw/compressed size: 910, ratio: 0.008823
Limits: MIN_INFLATE_RATIO: 0.010000, Entry: xl/styles.xml
	at org.apache.poi.openxml4j.util.ZipArchiveThresholdInputStream.checkThreshold(ZipArchiveThresholdInputStream.java:131)
	at org.apache.poi.openxml4j.util.ZipArchiveThresholdInputStream.read(ZipArchiveThresholdInputStream.java:81)
	at org.apache.poi.util.IOUtils.toByteArray(IOUtils.java:152)
	at org.apache.poi.util.IOUtils.toByteArray(IOUtils.java:121)
	at org.apache.poi.util.IOUtils.toByteArray(IOUtils.java:108)
	at org.apache.poi.openxml4j.util.ZipArchiveFakeEntry.<init>(ZipArchiveFakeEntry.java:47)
	at org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource.<init>(ZipInputStreamZipEntrySource.java:53)
	at org.apache.poi.openxml4j.opc.ZipPackage.<init>(ZipPackage.java:106)
	at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:301)
	at org.apache.poi.ooxml.util.PackageHelper.open(PackageHelper.java:37)
	at org.apache.poi.xssf.usermodel.XSSFWorkbook.<init>(XSSFWorkbook.java:303)
	... 120 common frames omitted

找问题

这一行的大致意思文件在读取后,占用的内存比预想中的要大很多,希望我们去设置参数来忽略此问题。
但这个问题不是设置参数能解决的。只能从导出代码找问题了。

如下导出代码的意思是循环将数据写入到excel表格中

    private void writeDataToExecl(List<ProfitCenterGroupTree> childrenAbles, XSSFWorkbook hssfWorkbook) {
        if (CollectionUtils.isEmpty(childrenAbles) || hssfWorkbook == null) {
            return;
        }
        XSSFSheet sheet = hssfWorkbook.getSheet(EXCL_SHEET_NAME);
        // 行偏移量
        final int rop = 1;
        // 列偏移量
        final int cop = 0;
        // 实际行坐标
        int index = rop;
        Iterator<ProfitCenterGroupTree> iterator = childrenAbles.iterator();
        // 循环写入:实际循环的项目是利润中心/组的内部用户信息。
        while (iterator.hasNext()) {
            ProfitCenterGroupTree next = iterator.next();
            Map<String, String> loginNames = next.getLoginNames();
        
        
            String type = TYPE_GRPPRC.equals(next.getType())?"利润中心组":"利润中心";
            //增加组分类提示信息
            for (String loginName : loginNames.keySet()) {
                String name = loginNames.get(loginName);
                // 写入利润中心/组
                POIUtils.insertCellValue(sheet, index, 0 + cop, next.getId(), POIUtils.getcellStyle(hssfWorkbook));
                // 写入利润中心/组描述
                POIUtils.insertCellValue(sheet, index, 1 + cop, next.getDesc(), POIUtils.getcellStyle(hssfWorkbook));
                // 写入分类
                POIUtils.insertCellValue(sheet, index, 2 + cop, type, POIUtils.getcellStyle(hssfWorkbook));
                // 写入用户编号
                POIUtils.insertCellValue(sheet, index, 3 + cop, loginName, POIUtils.getcellStyle(hssfWorkbook));
                // 写入用户名称
                POIUtils.insertCellValue(sheet, index, 4 + cop, name, POIUtils.getcellStyle(hssfWorkbook));
                // 写入变动标识(默认就是:“没有变动”)
                POIUtils.insertCellValue(sheet, index, 5 + cop, "没有变动", POIUtils.getcellStyle(hssfWorkbook));
                // 行坐标下移
                index++;
            }
        }
    }

重点关注这行代码。出问题的是这段代码中

// 对指定单元格赋值
// POIUtils.insertCellValue(sheet对象, 行,坐标 列坐标, 数值, 样式)
POIUtils.insertCellValue(sheet, index, 0 + cop, next.getId(), POIUtils.getcellStyle(hssfWorkbook));

其中方法 POIUtils.getCellStyle是一个封装起来获取默认样式的方法,每次都会从hssfWorkbook对象中取一个默认样式

public static XSSFCellStyle getcellStyle(XSSFWorkbook payHssfWorkbook) {
        return getcellStyle(payHssfWorkbook, null,BorderStyle.THIN);
    }
    public static XSSFCellStyle getcellStyle(XSSFWorkbook payHssfWorkbook, Short color ) {
        return getcellStyle(payHssfWorkbook, color,BorderStyle.THIN);
    }
    public static XSSFCellStyle getcellStyle(XSSFWorkbook payHssfWorkbook,BorderStyle borderStyle) {
        return getcellStyle(payHssfWorkbook, null,borderStyle);
    }
    public static XSSFCellStyle getcellStyle(XSSFWorkbook payHssfWorkbook, Short color , BorderStyle borderStyle) {
        XSSFCellStyle cellStyle = payHssfWorkbook.createCellStyle();//获取初始化单元格格式对象
        if (color != null) {
            cellStyle.setFillForegroundColor(color);//设置背景色
            cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        }
        if(borderStyle != null) {
        	 cellStyle.setBorderBottom(borderStyle);//底部边框颜色
             cellStyle.setBorderLeft(borderStyle);//底部边框颜色
             cellStyle.setBorderTop(borderStyle);//底部边框颜色
             cellStyle.setBorderRight(borderStyle);//底部边框颜色
        }
        cellStyle.setAlignment(HorizontalAlignment.CENTER);//居中
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直

        XSSFFont font = payHssfWorkbook.createFont();
        font.setFontName("宋体");
        font.setFontHeightInPoints((short) 10);//设置字体大小
        cellStyle.setFont(font);
        return cellStyle;
    }

我个人的理解:因为每次调用POIUtils.getCellStyle方法都会从Workbook中获取一个新的样式对象。这就导致循环生成了无数个样式对象在文件中,导出时没问题,是因为生成样式对象量没有到达一定限度到了就会报错:

java.lang.IllegalStateException: The maximum number of Cell Styles was exceeded. You can define up to 64000 style in a .xlsx Workbook

当导入时,poi会读取到这些样式对象。导致占用内存过大。

解决方案

因为从Workbook中获取的样式对象可以复用,只获取一个样式设定好之后就可以给无数个单元格使用。如果有多重不同的样式,在从workbook中获取就可以了。

修改后的代码如下:

    private void writeDataToExecl(List<ProfitCenterGroupTree> childrenAbles, XSSFWorkbook hssfWorkbook) {
        if (CollectionUtils.isEmpty(childrenAbles) || hssfWorkbook == null) {
            return;
        }
        XSSFSheet sheet = hssfWorkbook.getSheet(EXCL_SHEET_NAME);
        // 行偏移量
        final int rop = 1;
        // 列偏移量
        final int cop = 0;
        // 实际行坐标
        int index = rop;
        //在外层获取默认样式,循环直接使用即可
        XSSFCellStyle getcellStyle = POIUtils.getcellStyle(hssfWorkbook);
        
        Iterator<ProfitCenterGroupTree> iterator = childrenAbles.iterator();
        // 循环写入:实际循环的项目是利润中心/组的配置权限用户信息。
        while (iterator.hasNext()) {
            ProfitCenterGroupTree next = iterator.next();
            Map<String, String> loginNames = next.getLoginNames();

            // 当利润中心/组没有关联过用户,就不需要进行导出
            if (CollectionUtils.isEmpty(loginNames)) {
                continue;
            }
            String type = TYPE_GRPPRC.equals(next.getType())?"利润中心组":"利润中心";
            //增加组分类提示信息
            for (String loginName : loginNames.keySet()) {
                String name = loginNames.get(loginName);
                // 写入利润中心/组
                POIUtils.insertCellValue(sheet, index, 0 + cop, next.getId(), getcellStyle);
                // 写入利润中心/组描述
                POIUtils.insertCellValue(sheet, index, 1 + cop, next.getDesc(), getcellStyle);
                // 写入分类
                POIUtils.insertCellValue(sheet, index, 2 + cop, type, getcellStyle);
                // 写入用户编号
                POIUtils.insertCellValue(sheet, index, 3 + cop, loginName, getcellStyle);
                // 写入用户名称
                POIUtils.insertCellValue(sheet, index, 4 + cop, name, getcellStyle);
                // 写入变动标识(默认就是:“没有变动”)
                POIUtils.insertCellValue(sheet, index, 5 + cop, "没有变动", getcellStyle);
                // 行坐标下移
                index++;
            }
        }
    }

把 POIUtils.getcellStyle(hssfWorkbook);方法直接抽取到循环最外层供循环内的使用就好了

       //在外层获取默认样式,循环直接使用即可
        XSSFCellStyle getcellStyle = POIUtils.getcellStyle(hssfWorkbook);

问题解决!!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值