前言
公司存在很多excel含图片导出,因为提供给客户的excel可能还会打印出来,所以图片不能存链接。真坑!!!
一步步解决原理
-
使用这个SXSSFWorkbook进行excel导出,不了解的可以看我的这个博客
-
异步导出
- 使用异步导出方式,用户点击导出后后台自动运行导出,无需用户等待,然后有个任务列表,导出完成后用户可以回来点开下载
-
压缩导出
- 建议压缩图片导出:因为压缩图片后导出可以支撑更多的数据量,但还是因服务器内存而定。
- 不建议不压缩导出:因不压缩图片的数据量巨大,特别能吃内存。可能会导不出,不建议使用
-
SXSSFWorkbook图片没缓存在磁盘上
-
因为我们都知道SXSSFWorkbook有个操作窗口,默认100条数据在内存,多余的数据会缓存在磁盘中,追加图片的源码
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;
-
-
分页导出
- 缘由:即便是压缩导出,仍存在上限。假设每条记录含9张图片,每张图片大小为100k。一条记录算1mb,1000条数据基本占1g内存,而你的服务运行内存也就1g,也就说压缩最多导1000条就撑死了。
- 引出:既然一个excel承载不了这么多,那就把它拆成多个导出excel,比如200条一个excel,最后将生成的多个excel压缩成一个压缩包提供给用户下载。
- 结论:分页导出功能生成的压缩包里是多个较小的excel。因为即便导出一个很大的excel给到用户也没有意义,因为这跟打不开。多个较小可以较为流畅的打开。可以承载更大数据量,理论上磁盘有多大,就可以存多少excel。