影落离风

The shadow falls away from the wind

0%

大数据量excel含图片导出解决方案

前言

公司存在很多excel含图片导出,因为提供给客户的excel可能还会打印出来,所以图片不能存链接。真坑!!!

一步步解决原理

  1. 使用这个SXSSFWorkbook进行excel导出,不了解的可以看我的这个博客

    你一定没用过最简单的使用SXSSFWorkbook快速导出百万条数据

  2. 异步导出

    1. 使用异步导出方式,用户点击导出后后台自动运行导出,无需用户等待,然后有个任务列表,导出完成后用户可以回来点开下载
  3. 压缩导出

    1. 建议压缩图片导出:因为压缩图片后导出可以支撑更多的数据量,但还是因服务器内存而定。
    2. 不建议不压缩导出:因不压缩图片的数据量巨大,特别能吃内存。可能会导不出,不建议使用
  4. SXSSFWorkbook图片没缓存在磁盘上

    1. 因为我们都知道SXSSFWorkbook有个操作窗口,默认100条数据在内存,多余的数据会缓存在磁盘中,追加图片的源码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      public int addPicture(byte[] pictureData, int format) {
      // 获取所有图片
      int imageNumber = getAllPictures().size() + 1;
      XSSFPictureData img = (XSSFPictureData)createRelationship(XSSFPictureData.RELATIONS[format], XSSFFactory.getInstance(), imageNumber, true);
      try {
      OutputStream out = img.getPackagePart().getOutputStream();
      out.write(pictureData);
      out.close();
      } catch (IOException e){
      throw new POIXMLException(e);
      }
      pictures.add(img);
      return imageNumber - 1;
      }

      /**
      * Gets all pictures from the Workbook.
      *
      * @return the list of pictures (a list of {@link XSSFPictureData} objects.)
      * @see #addPicture(byte[], int)
      */
      public List<XSSFPictureData> getAllPictures() {
      if(pictures == null){
      List<PackagePart> mediaParts = getPackage().getPartsByName(Pattern.compile("/xl/media/.*?"));
      pictures = new ArrayList<XSSFPictureData>(mediaParts.size());
      for(PackagePart part : mediaParts){
      pictures.add(new XSSFPictureData(part, null));
      }
      }
      return pictures; //YK: should return Collections.unmodifiableList(pictures);
      }

      /**
      * excel中追加的图片以XSSFPictureData对象,存在内存中
      */
      private List<XSSFPictureData> pictures;
  5. 分页导出

    1. 缘由:即便是压缩导出,仍存在上限。假设每条记录含9张图片,每张图片大小为100k。一条记录算1mb,1000条数据基本占1g内存,而你的服务运行内存也就1g,也就说压缩最多导1000条就撑死了。
    2. 引出:既然一个excel承载不了这么多,那就把它拆成多个导出excel,比如200条一个excel,最后将生成的多个excel压缩成一个压缩包提供给用户下载。
    3. 结论:分页导出功能生成的压缩包里是多个较小的excel。因为即便导出一个很大的excel给到用户也没有意义,因为这跟打不开。多个较小可以较为流畅的打开。可以承载更大数据量,理论上磁盘有多大,就可以存多少excel。

终极

大量图片数据导出为excel导致内存溢出解决方案落地