当我们在实现excel导出时,在数据量过大的情况下,总是容易发生内存溢出的情况。我们可以使用POI提供的 SXSSFWorkbook 类来避免内存溢出。

注:基于POI4.10版本源码

以下是官方文档对SXSSF包的说明

SXSSF (package: org.apache.poi.xssf.streaming) is an API-compatible streaming extension of XSSF to be used when very large spreadsheets have to be produced, and heap space is limited. SXSSF achieves its low memory footprint by limiting access to the rows that are within a sliding window, while XSSF gives access to all rows in the document. Older rows that are no longer in the window become inaccessible, as they are written to the disk.

大致翻译如下

SXSSF是XSSF的一个与API兼容的流扩展,在需要生成非常大的电子表格时使用,堆空间有限。SXSSF通过限制对滑动窗口中的行的访问来实现其低内存占用,而XSSF允许访问文档中的所有行。当将旧行写入磁盘时,不再在窗口中的旧行变得不可访问。

使用示例

        // 内存中保持100条数据, 超出的部分刷新到磁盘上SXSSFWorkbook wb = new SXSSFWorkbook(100);Sheet sh = wb.createSheet();for(int rownum = 0; rownum < 1000; rownum++){Row row = sh.createRow(rownum);for(int cellnum = 0; cellnum < 10; cellnum++){Cell cell = row.createCell(cellnum);String address = new CellReference(cell).formatAsString();cell.setCellValue(address);}}// rownum < 900 的数据被刷新到磁盘,不能被随机访问for(int rownum = 0; rownum < 900; rownum++){Assert.assertNull(sh.getRow(rownum));}// 最后的100条数据仍然在内存中,可以随机访问for(int rownum = 900; rownum < 1000; rownum++){Assert.assertNotNull(sh.getRow(rownum));}FileOutputStream out = new FileOutputStream("d:\\sxssf.xlsx");wb.write(out);out.close();// 从磁盘上释放临时文件wb.dispose();

临时文件分析

wb.write(out)此行断点,debug运行到此处时,可以在windows路径C:\Users\ADMINI~1\AppData\Local\Temp\下发现类似以下格式的文件:

此文件就是被刷新到磁盘上的数据临时文件。此文件是怎么生成的呢?接下来我们就进入到源码分析的阶段。进入方法:

    /*** Sreate an Sheet for this Workbook, adds it to the sheets and returns* the high level representation.  Use this to create new sheets.** @return Sheet representing the new sheet.*/@Overridepublic SXSSFSheet createSheet(){return createAndRegisterSXSSFSheet(_wb.createSheet());}SXSSFSheet createAndRegisterSXSSFSheet(XSSFSheet xSheet){final SXSSFSheet sxSheet;try{sxSheet=new SXSSFSheet(this,xSheet);}catch (IOException ioe){throw new RuntimeException(ioe);}registerSheetMapping(sxSheet,xSheet);return sxSheet;}

再进入new SXSSFSheet(this,xSheet)方法:

    public SXSSFSheet(SXSSFWorkbook workbook, XSSFSheet xSheet) throws IOException {_workbook = workbook;_sh = xSheet;_writer = workbook.createSheetDataWriter();setRandomAccessWindowSize(_workbook.getRandomAccessWindowSize());_autoSizeColumnTracker = new AutoSizeColumnTracker(this);}

接着进入workbook.createSheetDataWriter()方法,最后我们会发现以下代码:

    public SheetDataWriter() throws IOException {_fd = createTempFile();_out = createWriter(_fd);}

由此我们知道,在创建sheet页时,就创建了临时文件目录(即每一个sheet页都会对应创建一个临时文件)。那么临时文件是建立在什么目录下,是否可以手动修改呢?我们继续跟进_fd = createTempFile()方法:

    public File createTempFile() throws IOException {return TempFile.createTempFile("poi-sxssf-sheet", ".xml");}

上面代码我们可以知道,POI会生成一个前缀为’poi-sxssf-sheet’,后缀为’xml’的临时文件来存放表格数据的DOM结构。
POI提供了TempFileCreationStrategy接口的默认实现DefaultTempFileCreationStrategy来决定临时文件生成的目录:

    private void createPOIFilesDirectory() throws IOException {// Identify and create our temp dir, if needed// The directory is not deleted, even if it was created by this TempFileCreationStrategyif (dir == null) {String tmpDir = System.getProperty(JAVA_IO_TMPDIR);if (tmpDir == null) {throw new IOException("Systems temporary directory not defined - set the -D"+JAVA_IO_TMPDIR+" jvm property!");}dir = new File(tmpDir, POIFILES);}createTempDirectory(dir);}

JAVA_IO_TMPDIR实际上是JVM的系统变量java.io.tmpdir,由此我们可以知道SXSSF默认获取了JVM的临时文件目录来作为自己存放临时文件的目录。所以我们可以通过以下三种方式(推荐采用第三种)改变SXSSF临时文件的目录:

  • 设置JVM系统变量 -Djava.io.tmpdir=xxx 此方法会改变JVM所有的临时文件目录。
  • 实现TempFileCreationStrategy的接口
  • DefaultTempFileCreationStrategy的构造方法提供了 dir参数的构造:
    public DefaultTempFileCreationStrategy(File dir) { this.dir = dir; }
    重新构造DefaultTempFileCreationStrategy实例传值给org.apache.poi.util.TempFile类:
SXSSFWorkbook wb = new SXSSFWorkbook(100);
//更变临时文件目录
TempFile.setTempFileCreationStrategy(new DefaultTempFileCreationStrategy(new File("H:\\Temp")));
Sheet sh = wb.createSheet();
...
...
临时文件的压缩

SXSSF在临时文件中刷新表数据(每页一个临时文件),这些临时文件的大小可以增长到非常大的值。例如,对于20 MB的CSV数据,临时XML的大小超过了1000MB。如果临时文件的大小有问题,可以告诉SXSSF使用gzip压缩:

SXSSFWorkbook wb = new SXSSFWorkbook(100);
TempFile.setTempFileCreationStrategy(new DefaultTempFileCreationStrategy(new File("H:\\Temp")));
//压缩临时文件
wb.setCompressTempFiles(true);

SXSSFWorkbook类中有以下判断,我们可以看出,当设置了以上参数,SXSSF会用GZIP的方式压缩临时文件:

    protected SheetDataWriter createSheetDataWriter() throws IOException {if(_compressTmpFiles) {return new GZIPSheetDataWriter(_sharedStringSource);}return new SheetDataWriter(_sharedStringSource);}

压缩后的临时文件如下:

可以看到,原本621MB的临时文件压缩后只有42MB。但是采用压缩显而易见的会影响到EXCEL导出的性能,期间的权衡应该以真实的业务场景来考虑。

实际上SXSSF所有对DOM文档的操作都直接映射在了XSSF上,只是在外层提供了刷新磁盘的功能。具体是如何实现的,且听下回分解。

基于流的EXCEL文件导出,SXSSFWorkbook源码解析相关推荐

  1. SpringBoot文件上传源码解析

    一.SpringMVC文件上传源码分析前言(这部分我觉得原作者写的很好) 该如何研究SpringMVC的文件上传的源码呢? 研究源码并不是仅仅知道程序是怎样运行的,而应该从宏观的角度.不同的立场去看待 ...

  2. .Net Core 认证系统之基于Identity Server4 Token的JwtToken认证源码解析

    介绍JwtToken认证之前,必须要掌握.Net Core认证系统的核心原理,如果你还不了解,请参考.Net Core 认证组件源码解析,且必须对jwt有基本的了解,如果不知道,请百度.最重要的是你还 ...

  3. Koom 解决hprof文件过大-源码解析

    解决hprof文件过大 Hprof文件通常比较大,分析OOM时遇到500M以上的hprof文件并不稀奇,文件的大小,与dump成功率.dump速度.上传成功率负相关,且大文件额外浪费用户大量的磁盘空间 ...

  4. Python数据分析实战-将一维列表和二维列表内容保存到本地excel文件(附源码和实现效果)

    前面我介绍了可视化的一些方法以及机器学习在预测方面的应用,分为分类问题(预测值是离散型)和回归问题(预测值是连续型).同时做了关于图像识别的系列文章,让读者理解python进行图像识别的过程.原理和方 ...

  5. Rocksdb Compaction 源码详解(一):SST文件详细格式源码解析

    文章目录 前言 comapction流程概述 SST 文件细节 Footer meta index block filter meta block index meta block Compressi ...

  6. ServletContext的应用(共享数据、获取初始化参数、请求转发、读取资源文件)【源码解析】

    ServletContext应用 1.共享数据 我在这个Servlet中保存的数据,可以在另外一个Servlet中拿到 public class HelloServlet extends HttpSe ...

  7. Mybatis 源码解析 -- 基于配置的源码解析(二)

    为什么80%的码农都做不了架构师?>>>    mapper解析 接着上篇的配置,本篇主要讲解mappers标签 <?xml version="1.0" e ...

  8. SpringBoot入门-源码解析(雷神)

    一.Spring Boot入门 视频学习资料(雷神): https://www.bilibili.com/video/BV19K4y1L7MT?p=1 github: https://github.c ...

  9. 【机器学习】word2vec学习笔记(一):word2vec源码解析

    0. word2vec地址 官网地址:https://code.google.com/archive/p/word2vec/ GitHub地址:https://github.com/tmikolov/ ...

最新文章

  1. Uber将整体式API拆分为微服务
  2. java notifier_Java学习笔记---4.Java的分支循环语句
  3. 固态器件理论(5)PN结
  4. 陕西小学三年级计算机下册教案,小学三三年级信息技术下册教学计划
  5. shell换行合并多个文件_如何合并多个pdf文件?这里有合并PDF最简单的方法
  6. 一道超级复杂的js题目
  7. 《Linux高性能服务器编程》学习总结(四)——TCP/IP通信案例:访问Internet上的Web服务器...
  8. swift自行车品牌介绍_品牌101:简介
  9. Spring Data Redis—Pub/Sub(附Web项目源码)
  10. .NET Core开源任务调度平台ScheduleMaster上新了
  11. 蓝桥杯 ADV-132 算法提高 笨小猴
  12. turtlebot3_teleop_key 键盘控制程序,使之0速度时不会一直发送 topic,通过topic代替键盘控制小车运动。
  13. 利用哈夫曼编码英文字母表
  14. MySQL函数大全及用法
  15. WEB前端开发练习代码
  16. c语言追踪机械腿位置,类人足球机器人动作规划与自适应轨迹跟踪算法研究.pdf...
  17. 计算机在军事方面的应用
  18. 如何使用PDFelement 6 Pro Mac中的OCR功能
  19. thinkadmin V6 Experiences
  20. 学籍管理系统制作教程第一天

热门文章

  1. 跟随者数字解码_跟随模式的数字
  2. abap 添加alv上的工具栏的按钮_神器必会!“世界上最好的编辑器Source Insight”...
  3. 计算机硬件加速怎么开,显卡硬件加速,小编教你电脑怎么开启显卡硬件加速
  4. zzz,zzz,zz9_ZZZ的完整形式是什么?
  5. Python operator.not_()函数与示例
  6. Java Integer类shortValue()方法与示例
  7. SpringBoot 如何统一后端返回格式?老鸟们都是这样玩的!
  8. 7种可能会导致内存泄漏的场景!
  9. Oracle plsql 月历
  10. 猫版超级玛丽 附下载