基于流的EXCEL文件导出,SXSSFWorkbook源码解析

当我们在实现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上,只是在外层提供了刷新磁盘的功能。具体是如何实现的,且听下回分解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/557052.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【POI】读取Excel表中的数据

确认需要读取的表格的内容: 导入依赖&#xff1a; <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.1</version></dependency>package com.example;import org.apache.poi.s…

w15php系列之基础类型

一、计算100之内的偶数之和 实现思路 所有的偶数除2都为0 代码实现 <?php # 记录100以内的偶数和 $number1; $num0; while($number<100){if($number%20){ $num$number;}$number1; } echo $num; ?>输出的结果 二、计算100之内的奇数之和 实现思路 所有的奇数除…

android 魅族系统升级,魅族Flyme6底层安卓系统升级到了Android7.0/7.1,为何时间这么久?...

国产手机系统中&#xff0c;小米的MIUI和魅族的flyme有着众多的用户。相比魅族的系统来说小米MIUI在更新速度上更胜一筹&#xff0c;开玩笑的讲&#xff0c;小米系统已经升级到了MIUI9而魅族仅仅才Flyme6(当然&#xff0c;这更多的是调侃)。不过也从侧面看出&#xff0c;魅族在…

SVN 回滚(撤回)提交的代码

一、 TortoiseSVN -> Show log 二、 注意&#xff1a;步骤二只是让你本地的代码回滚&#xff08;撤回&#xff09;到你未提交前的一个版本&#xff0c;并不会更新到SVN服务器上&#xff0c;也就是说你的同事依然能更新到你错误提交的代码。 右键点击你想撤回的提交 -&g…

Navicat工具获取操作数据库和表的SQL语句

工具 Navicat Premium 15MySQL服务器技术 SQL语句MySQL 在使用Navicat Premium 15客户端时&#xff0c;对数据库和表进行操作&#xff0c;会产生SQL语句。有时我们不记得SQL语句如何编写&#xff0c;可以直接从工具中获取SQL语句。下面利用实例说明获取SQL语句的几种方法&…

html自动适应屏幕分辨率,css如何自适应屏幕大小?

css如何自适应屏幕大小&#xff1f;下面本篇文章给大家介绍一下使用CSS实现屏幕大小自适应的方法。有一定的参考价值&#xff0c;有需要的朋友可以参考一下&#xff0c;希望对大家有所帮助。css如何自适应屏幕大小&#xff1f;要想实现css屏幕大小自适应&#xff0c;首先得引入…

Navicat Premium 12连接SQLServer[ODBC驱动程序管理器]未发现数据源名称并且未指定默认驱动程序

报错截图&#xff1a; 解决办法&#xff1a; 右击安装图标&#xff0c;点击“打开文件所在位置” 找到下图程序&#xff0c;按自己情况安装 全都默认“下一步”就行。 安装完成后&#xff0c;关闭程序重新打开就可以了。

Springboot的异步、定时、邮件任务

一、异步任务 ​ 1、编写一个类AsyncService ​ 异步处理还是非常常用的&#xff0c;比如我们在网站上发送邮件&#xff0c;后台会去发送邮件&#xff0c;此时前台会造成响应不动&#xff0c;直到邮件发送完毕&#xff0c;响应才会成功&#xff0c;所以我们一般会采用多线程的…

raml2html 安装,Raml实践

Raml实践简介&#xff1a;RAML的全称是RESTful API Modeling Language&#xff0c;这是一种用来描述基于Restful架构的API(设计API)的语言。它的语法规范是基于YAML的新规范&#xff0c;因此机器与人类都能够轻易地理解其中的内容。一、工具安装一、API Workbench(客户端工具-推…

Spring框架中的单例Bean是线程安全的吗?

首先直接给出答案&#xff1a;不是线程安全的 一、分析问题 证明不是线程安全的案例如下&#xff1a; public class Student {private String stuName;public String report(String uname){stuName "大家好&#xff0c;我叫&#xff1a;"uname;try {Thread.sleep…

三诺+n20g+微型计算机,原来是他?揭秘三诺永恒系列开山鼻祖

提起三诺&#xff0c;我们的脑海中常常会浮现出几个关键词&#xff0c;“高性价比”、 “独立功放”、“摩机大赛”等等&#xff0c;摩机大赛让我们对三诺永恒系列2.0音箱有了全新的认识&#xff0c;凭借出色的音质表现&#xff0c;永恒系列广受专业编辑和用户的好评&#xff0…

Spring 中的bean 是线程安全的吗?

结论&#xff1a;不是线程安全的 Spring容器中的Bean是否线程安全&#xff0c;容器本身并没有提供Bean的线程安全策略&#xff0c;因此可以说Spring容器中的Bean本身不具备线程安全的特性&#xff0c;但是具体还是要结合具体scope的Bean去研究。 Spring 的 bean 作用域&#…

天文学专业在什么时候学计算机,南京大学在985排名第几?南京大学最牛的专业是天文系吗?...

选择科目测一测我能上哪些大学选择科目领取你的专属报告>选择省份关闭请选择科目确定v>百年名校南京大学坐落于金陵古都&#xff0c;文化底蕴深厚&#xff0c;是不少考生向往的“211”“985”“双一流”高校。本期圆梦志愿就来带大家看看这所学校在全国985高校中的排名情…

Java中的ThreadLocal详解

一、ThreadLocal简介 多线程访问同一个共享变量的时候容易出现并发问题&#xff0c;特别是多个线程对一个变量进行写入的时候&#xff0c;为了保证线程安全&#xff0c;一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal是除了加锁这种同…

谈谈对ThreadLocal的理解?(基于jdk1.8)

在java的多线程模块中&#xff0c;ThreadLocal是经常被提问到的一个知识点&#xff0c;提问的方式有很多种&#xff0c;可能是循序渐进也可能是就像我的题目那样&#xff0c;因此只有理解透彻了&#xff0c;不管怎么问&#xff0c;都能游刃有余。 这篇文章主要从以下几个角度来…

简单理解BigDecimal.valueof(Double t)与BigDecimal.valueof(String t)的区别——BigDecimal

上面的代码主要的区别在于 初始化BigDecimal时形参是double、String和float的区别 从上面可以看到&#xff0c;当double 和 float 时&#xff0c;实际保存的值并不是是准确的0.99&#xff0c;这是为什么呢 大致的原因是&#xff1a; BigDecimal(double val)将会把double型二…

下图中的蓝月亮为科学家用计算机,2018年高一地理前半期课时练习试卷带答案和解析...

目前人类可以观察到的最高级别天体系统是A. 总星系 B. 银河系 C. 太阳系 D. 地月系【答案】A【解析】本题考查天体系统的层次。距离相近的天体因相互吸引和相互绕转&#xff0c;构成不同级别的天体系统&#xff0c;天体系统的层次为&#xff1a;最高一级为总星系(即目前所知的宇…

HashMap的put方法返回值问题

API文档中的描述&#xff1a; 先看一个例子 Map<Character, Integer> map new HashMap<Character, Integer>(); System.out.println(map.put(a, 0)); // null System.out.println(map.put(a, 1)); // 0 System.out.println(map.put(a, 2)); // 1 System.out.pri…

掌上通计算机一级考试在线安装,计算机一级掌上通

计算机一级掌上通app是一款计算机等级考试学习的软件&#xff0c;让你在线学习计算机的操作知识&#xff0c;便于通过等级考试&#xff0c;快速准确&#xff1b;软件提供海量选择题的题库&#xff0c;随时随地做题&#xff0c;简单又方便&#xff0c;还有计算机基本操作讲解&am…

东莞理工学院计算机ccf,中国计算机学会东莞分部成立

为更全面和更好地服务东莞计算机领域专业人士的学术和职业发展&#xff0c;在中国计算机学会(CCF)总部和广州、深圳分部的指导和协助下&#xff0c;由东莞理工学院和中美融易孵化器牵头&#xff0c;联合东莞市各大高校、学会、企业&#xff0c;共同发起成立中国计算机学会东莞分…