基于流的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,一经查实,立即删除!

相关文章

android设置错误页面,Android ViewPager设置当前项目/页面时抛出IndexOutOfBounds异常

我有一个ViewPager有三个项目.我试图设置ViewPager查看最右边的页面(这将是第二个元素).这是返回IndexOutOfBounds异常,虽然我知道索引应该在边界.这是确切的堆栈&#xff1a;02-22 12:22:50.256: E/AndroidRuntime(384): FATAL EXCEPTION: main02-22 12:22:50.256: E/AndroidR…

【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 从本地服务器下载文件,Retrofit2-如何从服务器下载文件

在这篇博客中&#xff0c;将会讲述使用Retrofit十分需要的一个功能&#xff1a;怎么去下载文件&#xff0c;下面会展示一些下载文件需要写的代码片段&#xff0c;从小的 png 图片到大的 zip文件。

Java操作Excel中HSSFCell.CELL_TYPE_STRING、BOOLEAN、NUMERIC无定义解决方法

错误原因&#xff1a;jar包版本更新&#xff0c;官方改动&#xff1b; 解决方法&#xff1a; 导入CellType包import org.apache.poi.ss.usermodel.CellType 使用CellType.STRING代替HSSFCell.CELL_TYPE_STRING 其他同理。 新版&#xff1a; if(cellnull||cell.equals(null)…

android x86_64 服务器运行,魔趣 android10 编译x86-64 运行模拟器

cd 代码目录. build/envsetup.shlunch出现1. aosp_arm-eng2. aosp_arm64-eng3. aosp_car_arm-userdebug4. aosp_car_arm64-userdebug5. aosp_car_x86-userdebug6. aosp_car_x86_64-userdebug7. aosp_x86-eng8. aosp_x86_64-eng9. car_x86_64-userdebug10. m_e_arm-userdebug11.…

RoundingMode 几个参数详解

第一版 java.math().RoundingMode里面有几个参数搞得我有点晕&#xff0c;现以个人理解对其一一进行总结&#xff1a; RoundingMode.CEILING&#xff1a;取右边最近的整数 RoundingMode.DOWN&#xff1a;去掉小数部分取整&#xff0c;也就是正数取左边&#xff0c;负数取右边…

android资源透明背景,@谷歌android帝 这是你想要的,全局透明背景教程,两种方法...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼首先大家提取原生态没有任何修改过的framework-res.apk&#xff0c;反编译&#xff0c;(这里特别交代一下&#xff0c;大家要看仔细了&#xff0c;全局背景化透明化的修改是有两种修改的方法&#xff0c;大家可以根据自己喜好来修改…

关于异常nested exception is java.lang.NoClassDefFoundError: org/apache/commons/fileupload/FileItemFactor

简单看了一下发现原来是没有commons-fileupload包导致的&#xff0c;然后就在pom里加入了依赖。 <dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version></dep…

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;首先得引入…

鸿蒙之始有几个老婆,先天五太

本词条缺少概述图&#xff0c;补充相关内容使词条更完整&#xff0c;还能快速升级&#xff0c;赶紧来编辑吧&#xff01;先天五太最早见于东汉今文经谶纬神学《周易乾凿度》&#xff0c;后见于西晋《列子》&#xff0c;被定为道家哲学中代表无极过渡到天地诞生前的五个阶段。分…

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

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

html 冒泡事件拦截,Js 冒泡事件阻止

一. 事件目标现在&#xff0c;事件处理程序中的变量event保存着事件对象。而 event.target 属性保存着产生事件的目标元素。这个属性是 DOM API 中规定的&#xff0c;但是没有被所有浏览器实现 。jQuery对这个事件对象进行了必要的扩大&#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…