Apache POI处理EXCEL中的图片

1  背景

最近做需求的时候需要做一个导入的功能,通过调研一些java导入的第三方jar包,最后选择了阿里的easyExcel,按照约定的规范能够简单快速的处理excel中的信息,通过@ExcelProperty注解和表头对应解析出excel的信息,使用的过程中发现了两个问题。

  1. 第一个是如果excel中某一行完全是空行的话,excel会直接跳过这一行信息(不知道为什么要这么设计,对于某些业务来说带来一定的不方便性。)
  2. easyExcel不支持导入图片的信息,完全处理不了图片的信息。在github上也得到了回答,不支持处理图片信息

2  Apache Poi实践

为了处理图片信息,后面在优化的过程中选择用poi处理excel的图片信息,这里做个demo的总结。

public void saveImg(InputStream inputStream) throws Exception {
        Map<String, List<PictureData>> pictureMap = ExcelUtil.getWorkbook(inputStream);

        Object[] key = pictureMap.keySet().toArray();
        for (int i = 0; i < pictureMap.size(); i++) {
            // 获取图片索引
            String picIndex = key[i].toString();
            // 获取图片流
            List<PictureData> pictureDataList = pictureMap.get(picIndex);
            if (CollectionUtils.isNotEmpty(pictureDataList)) {
                pictureDataList.forEach(
                        pictureData -> {
                            byte[] data = pictureData.getData();
                            //图片保存路径
                            String imgPath = "D:\\image" + System.currentTimeMillis();
                            FileOutputStream out = null;
                            try {
                                out = new FileOutputStream(imgPath);
                                out.write(data);
                                out.close();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        });
            }
        }
    }
   public static Map<String, List<PictureData>> getWorkbook(InputStream inputStream) {
        try {
            // 这里要注意下excel表格的格式,不要有多余的空行的情况,否则这里可能会内存溢出,建议用高版本的easyExcel拿到excel的对象。
            Workbook workbook = WorkbookFactory.create(inputStream);
            Sheet sheet = workbook.getSheetAt(0);
            if (sheet instanceof HSSFSheet) {
                return getXlsPictures((HSSFSheet) sheet);
            } else if (sheet instanceof XSSFSheet) {
                return getXlsxPictures((XSSFSheet) sheet);
            } else {
                return Maps.newHashMap();
            }
        } catch (Exception e) {
            return Maps.newHashMap();
        }
    }
 /**
     * 获取图片和位置 (xls)
     *
     * @param sheet
     * @return
     */
    public static Map<String, List<PictureData>> getXlsPictures(HSSFSheet sheet) {
        Map<String, List<PictureData>> map = new HashMap<>();
        List<HSSFShape> list = sheet.getDrawingPatriarch().getChildren();
        for (HSSFShape shape : list) {
            if (shape instanceof HSSFPicture) {
                HSSFPicture picture = (HSSFPicture) shape;
                HSSFClientAnchor cAnchor = (HSSFClientAnchor) picture.getAnchor();
                String coordinate = cAnchor.getRow1() + "-" + cAnchor.getCol1();
                // 处理一个单元格内有多个图片的情况
                if (CollectionUtils.isNotEmpty(map.get(coordinate))) {
                    map.get(coordinate).add(picture.getPictureData());
                } else {
                    map.put(coordinate, Lists.newArrayList(picture.getPictureData()));
                }
            }
        }
        return map;
    }
/**
     * 获取图片和位置 (xlsx)
     *
     * @param sheet
     * @return
     */
    public static Map<String, List<PictureData>> getXlsxPictures(XSSFSheet sheet) {
        Map<String, List<PictureData>> map = new HashMap<>();
        List<POIXMLDocumentPart> list = sheet.getRelations();
        for (POIXMLDocumentPart part : list) {
            if (part instanceof XSSFDrawing) {
                XSSFDrawing drawing = (XSSFDrawing) part;
                List<XSSFShape> shapes = drawing.getShapes();
                for (XSSFShape shape : shapes) {
                    XSSFPicture picture = (XSSFPicture) shape;
                    XSSFClientAnchor anchor = (XSSFClientAnchor) shape.getAnchor();
                    // 获取excel的行列当作map的key
                    CTMarker marker = anchor.getFrom();
                    String coordinate = marker.getRow() + "-" + marker.getCol();
                    // 处理一个单元格内有多个图片的情况
                    if (CollectionUtils.isNotEmpty(map.get(coordinate))) {
                        map.get(coordinate).add(picture.getPictureData());
                    } else {
                        map.put(coordinate, Lists.newArrayList(picture.getPictureData()));
                    }
                }
            }
        }
        return map;
    }

3  注意事项

1: 利用【XSSFClientAnchor anchor = picture.getPreferredSize()】这种方式获取图片所在坐标时会报空指针异常(在图片粘贴的时候覆盖了单元格之间分界线的情况会有问题),应该用【XSSFClientAnchor anchor = (XSSFClientAnchor) shape.getAnchor()】这种方式获取坐标。

2:【Workbook workbook = WorkbookFactory.create(inputStream)】这个方法获取Workbook会很吃机器内存,在excel有很多空行(看似空行,没有数据,其实excel认为不是空行)的时候可能会内存溢出,服务器卡死。

比如要导入的数据是下图所示,不要有多余的空行。

3:导入表格之前先要确定下excel表格的最后一行在哪(可以用ctrl+end确定)?如果发现你的表格的最后一行定位到了空数据的行,如:

你可以选中需要处理的行,然后清除下格式:

 否则系统在用poi处理excel会导致内存溢出,cpu打满(其实是gc线程一直在尝试释放内存)。服务器会在短时间内拒绝服务!!!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值