Poi版本升级优化

2024-08-26 14:48
文章标签 优化 poi 版本升级

本文主要是介绍Poi版本升级优化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Poi-3.17前后版本api使用差异

1. 升级缘由

最近公司prod环境出现因为Excel文件下载数据量过大导致应用out of memory, 然后就需要找到内存溢出的原因及优化方案. 经分析, 得出以下结论:

1.1 事故原因

(1) 应用场景发生在页面列表查询功能, 可以批量勾选列表项选择下载, 后台又需要根据勾选的列表项字段关联查询出N+条大量明细数据, 系统程序又没有对大数据量做评估校验, 一路绿灯,最终形成社会性死亡现场 . 导致宕机的主要原因倒不是因为单个请求线程的数据量过大导致, 当前请求最多不过是下载失败, 真正导致应用宕机是因为前端界面一直展示下载加载效果, 用户一直重复点击, 多个请求线程访问应用, 熟悉POI的老铁们应该都知道, POI生成DOM节点需要消耗大量的内存, 结果就是开头的out of memory了.

(2) 系统监控平台发现大量接口请求超时, 为了不影响后续其他业务的正常作业, 立即展开抢救, 过程就是根据导致宕机的请求数据找到操作用户(因为是内网用户, 可以查到用户联系方式), 然后打电话给那位用户说明情况并让其不要再重复点击, 最后就是重启Server了, 很多问题都是重启可以搞定的哈, 当然我们是主从2台机器, 所以停机重启不会大面积影响业务, 请求会经F5负载均衡转发到另外一台正常运行的机器上去 .

(3) 经过在测试环境复现事故场景, 发现超过65536条(含标题行)记录就会报错, 相信用过Poi的老铁们都知道了, 我们程序使用的是POIHSSF创建的Workbook工作簿, 也就是创建的2003版Excel文件(xls), 最多仅支持65536行记录写入(0-65535), 超过这个量的数据下载肯定会报错了. 下面是我自己测试复现的报错截图:

image-20210919110849540

1.2 优化方案

分析出上面的问题原因后, 我也查阅了操作Excel相关的技术, 尝试了下面几种解决方案.

1.2.1 改用XSSF生成

针对上面HSSF只能写入65536行记录的局限, POI也是给出了解决方案的, XSSF创建的Workbook工作簿, 生成的是2007版Excel(xlsx), 支持上限1048576行记录的写入 , 基本满足我们日常的应用场景了. 另外在XSSF基础上, POI-3.8版本开始提供的支持低内存占用的操作方式SXSSFWorkbook, 支持2007以上版本的Excel操作.

关键代码体现 :

/*** 根据要生成的文件类型创建HSSF或者XSSF工作簿* @param fileType .xls .xlsx* @return*/
public static Workbook getWorkbook(String fileType) {Workbook wb = null;switch (fileType) {case CSISCONSTANT.EXCEL03_EXTENSION:wb = new HSSFWorkbook(); // 创建工作簿 2003版excelbreak;case CSISCONSTANT.EXCEL07_EXTENSION:default:wb = new XSSFWorkbook(); // 创建工作簿 2007版excelbreak;}return wb;
}

具体实现代码请移步博客Poi实现Excel导出

1.2.2 数据拆分文件压缩

XSSF方案只是解决了大数据量写入的问题, 从系统安全性和性能方面考虑, 还有更多的优化空间. 程序现状没有对数据做分流处理, 依然是全部数据一次性生成并写入Excel文件, 对内存的消耗仍旧很糟糕. 其实可以对查询出来的大量数据根据自己设置的阈值做分流处理, 阈值设置在1万-2万之间, 因为打开一个Excel文件当前Sheet页方便快速翻阅查看最好了, 数据写入太多翻页查看数据会很卡, 用户体验不好. 根据阈值分流后的数据在性能允许范围内, 循环调用提前封装好的生成Excel文件的方法, 每次循环生成后Excel文件后, 会释放掉POI消耗的内存, 相比较一次性生成并写入Excel文件内存的占用时间和消耗小很多. 待所有Excel文件生成完成, 最后将这些文件打包压缩成zip文件流返回给界面.

另外, SpringMVC中的Web端文件下载是在当前请求线程内完成的, 在生成并写入数据文件的过程中, 界面只能等着后端系统的响应, 且当前请求的线程会一直被占用着, 数据量不大还好, 如果数据量很大会影响用户体验. 我工作中用到的是前后端分离的项目, 文件下载是异步请求实现的, 设计思想就是界面发起文件下载的异步请求, 后台程序将生成的数据文件存放到服务器临时下载目录中 , 将临时下载目录的数据文件路径以流的形式返回给前端界面, 前端使用封装好的api直接去服务器临时下载目录中下载文件即可, 而不需要将整个数据文件以流的形式返回给前端界面. 文件上传也可以采用这种思想, 前端将文件直接上传到服务器临时上传目录中, 将文件路径提交到后台系统, 后台程序直接去服务器临时上传目录中读取解析.

数据分流生成Excel的实现代码请移步博客 数据分流写入Excel

文件压缩的实现代码请移步博客 Poi实现Excel导出

1.2.3 使用开源技术EasyExcel

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百MB的Excel文件. 阿里的大牛们对POI进行了各种封装和优化, 不管是性能还是内存消耗方面都是质的飞跃, 在api使用方面也极具人性化, 简单易用.

说明: EasyExcel底层是依赖POI的, 最低版本的EasyExcel要求POI-3.17版本, 如果我用这个方案, 必然面临POI版本升级问题, 因为POI-3.17版本相比POI3.17以下的版本, 在样式设置和单元格数据类型设置等方面改动比较大, 很多API进行了抽取. 项目组的系统有很多文件下载功能, 不能因为这个场景的问题全面改造, 没有测试人力的全面支持, 无法形成有效的升级. 后面我采用了数据分流写入并压缩的方案, 避免了POI版本升级的麻烦. 但是作为一名技术宅男, 肯定是不会放过这个优化方案的, 自己业余时间搭建了demo进行了冲突aip的升级改造, 具体冲突api的使用请见 Poi升级 .

EasyExcel请移步博客 EasyExcel学习笔记

1.2.4 其他

除了原生POI, EasyExcel外, 还有其他相关技术可以实现Excel的操作, 比如EasyPoi, Hutool-poi, jxl等.

EasyPoi请移步博客 EasyPoi学习笔记

Hutool-poi请参考Hutool官方文档中的office文档操作部分.

2. Poi升级

POI3.17与POI旧版本对比, 有很多API的使用改变了, 下面将依次列出它们的不同及新API的使用.

2.1 颜色定义

旧版本

HSSFColor.GREEN.index
HSSFColor.BLACK.index   

新版本

IndexedColors.GREEN.getIndex()
IndexedColors.GREEN.index
IndexedColors.BLACK.getIndex()
IndexedColors.BLACK.index

2.2 获取单元格格式

旧版本

// 获取单元格格式
int cellType = cell.getCellType(); 
// 与之对应的单元格格式int值 
HSSFCell.CELL_TYPE_BLANK  // 空
HSSFCell.CELL_TYPE_STRING // 字符串
HSSFCell.CELL_TYPE_NUMERIC // 数字类型   
HSSFCell.CELL_TYPE_BOOLEAN // 布尔
HSSFCell.CELL_TYPE_FORMULA // 公式   
HSSFCell.CELL_TYPE_ERROR // 错误   

新版本

CellType cellTypeEnum = cell.getCellTypeEnum(); // 获取单元格格式
// 与之对应的单元格格式枚举值 
CellType.BLANK // 空
CellType.STRING // 字符串   
CellType.NUMERIC // 数字类型   
CellType.BOOLEAN // 布尔   
CellType.FORMULA // 公式
CellType.ERROR // 错误   

2.3 设置单元格数据类型

旧版本

Cell cell = row.getCell(0); // 获取单元格对象
cell.setCellType(Cell.CELL_TYPE_STRING); // 设置单元格为字符串类型

新版本

Cell cell = row.getCell(0); // 获取单元格对象
cell.setCellType(CellType.STRING); // 设置单元格为字符串类型

2.4 设置单元格样式

单元格样式包含垂直居中样式, 边框样式, 背景填充颜色, 边框线条等…

旧版本

HSSFCellStyle cellStyle = wb.createCellStyle();
// XSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setFillForegroundColor(HSSFColor.LIME.index); // 标题行背景色为绿色
cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND); // 填充背景色
cellStyle.setAlignment(XSSFCellStyle.ALIGN_CENTER); // 文字水平居中
cellStyle.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER); // 文字垂直居中
cellStyle.setBorderBottom(XSSFCellStyle.BORDER_THIN); // 底部边框实体线条
cellStyle.setBorderTop(XSSFCellStyle.BORDER_THIN);  // 顶部边框实体线条
cellStyle.setBorderLeft(XSSFCellStyle.BORDER_THIN);  // 左部边框实体线条
cellStyle.setBorderRight(XSSFCellStyle.BORDER_THIN);  // 右部边框实体线条

新版本

CellStyle cellStyle = wb.createCellStyle();
// XSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setFillForegroundColor(IndexedColors.LIME.index); // 标题行背景色为绿色
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 填充背景色
cellStyle.setAlignment(HorizontalAlignment.CENTER); // 文字水平居中
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 文字垂直居中
cellStyle.setBorderBottom(BorderStyle.THIN); // 底部边框实体线条
cellStyle.setBorderTop(BorderStyle.THIN);  // 顶部边框实体线条
cellStyle.setBorderLeft(BorderStyle.THIN);  // 左部边框实体线条
cellStyle.setBorderRight(BorderStyle.THIN);  // 右部边框实体线条

2.5 合并单元格

旧版本

Sheet sheet = workbook.createSheet("sheet1");
// 起始行,结束行,起始列,结束列
sheet.addMergedRegion(new CellRangeAddress(1, 1,(short) 0, (short) 0));

新版本

Sheet sheet = workbook.createSheet("sheet1");
// 起始行,起始列,结束行,结束列
sheet.addMergedRegion(new Region(1, (short) 0, 1,(short) 0));

2.6 设置字体加粗

旧版本

Font font = workbook.createFont();
// font.setBoldweight((short) 400);
font.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
font.setBoldweight(XSSFFont.BOLDWEIGHT_NORMAL);
font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

新版本

Font font = workbook.createFont();
font.setBold(true);

2.7 设置图片属性

旧版本

// anchor主要用于设置图片的属性
XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 255, 255, (short) i, rowNum + 1,(short) i + 1, rowNum + 2);
anchor.setAnchorType(3);
// 插入图片
patriarch.createPicture(anchor,workbook.addPicture(byteArrayOut.toByteArray(), ClientAnchor.MOVE_DONT_RESIZE));

新版本

// anchor主要用于设置图片的属性
XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 255, 255, (short) i, rowNum + 1,(short) i + 1, rowNum + 2);
anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE));
// 插入图片
patriarch.createPicture(anchor,workbook.addPicture(byteArrayOut.toByteArray(), ClientAnchor.AnchorType.MOVE_DONT_RESIZE.value));

2.8 小结

poi版本升级问题产生报错汇总(后续待补充)

poi3.17之前版本poi3.17+版本用途
Cell.CELL_TYPE_STRINGCellType.STRING单元格数据格式判断
HSSFCell.CELL_TYPE_NUMERICCellType.NUMERIC单元格数据格式判断
CellStyle.ALIGN_CENTERHorizontalAlignment.CENTER单元格水平居中
CellStyle.VERTICAL_CENTERVerticalAlignment.CENTER单元格垂直居中
HSSFColor.GREY_25_PERCENT.indexIndexedColors.GREY_25_PERCENT.index设置图案颜色
CellStyle.SOLID_FOREGROUNDFillPatternType.SOLID_FOREGROUND设置图案样式
CellStyle.BORDER_THINBorderStyle.THIN边框
ClientAnchor.MOVE_DONT_RESIZEAnchorType.MOVE_DONT_RESIZE.value单元格插入图片

相关推荐

数据分流写入Excel

Poi版本升级优化

StringTemplate实现Excel导出

Poi模板技术

SAX方式实现Excel导入

DOM方式实现Excel导入

Poi实现Excel导出

EasyExcel实现Excel文件导入导出

EasyPoi实现excel文件导入导出

个人博客

欢迎各位访问我的个人博客: https://www.crystalblog.xyz/

备用地址: https://wang-qz.gitee.io/crystal-blog/

这篇关于Poi版本升级优化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1108870

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

MySQL高性能优化规范

前言:      笔者最近上班途中突然想丰富下自己的数据库优化技能。于是在查阅了多篇文章后,总结出了这篇! 数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 数据库对象的命名要能做到见名识意,并且最后不要超过32个字符 临时库表必须以tmp_为前缀并以日期为后缀,备份

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

构建高性能WEB之HTTP首部优化

0x00 前言 在讨论浏览器优化之前,首先我们先分析下从客户端发起一个HTTP请求到用户接收到响应之间,都发生了什么?知己知彼,才能百战不殆。这也是作为一个WEB开发者,为什么一定要深入学习TCP/IP等网络知识。 0x01 到底发生什么了? 当用户发起一个HTTP请求时,首先客户端将与服务端之间建立TCP连接,成功建立连接后,服务端将对请求进行处理,并对客户端做出响应,响应内容一般包括响应

DAY16:什么是慢查询,导致的原因,优化方法 | undo log、redo log、binlog的用处 | MySQL有哪些锁

目录 什么是慢查询,导致的原因,优化方法 undo log、redo log、binlog的用处  MySQL有哪些锁   什么是慢查询,导致的原因,优化方法 数据库查询的执行时间超过指定的超时时间时,就被称为慢查询。 导致的原因: 查询语句比较复杂:查询涉及多个表,包含复杂的连接和子查询,可能导致执行时间较长。查询数据量大:当查询的数据量庞大时,即使查询本身并不复杂,也可能导致

MySQL 数据优化

MySQL 数据优化的指南 MySQL 数据库优化是一个复杂且重要的过程,它直接影响到系统的性能、可靠性和可扩展性。在处理大量数据或高并发请求时,数据库的优化尤为关键。通过合理的数据库设计、索引使用、查询优化和硬件调优,可以大幅提高 MySQL 的运行效率。本文将从几个主要方面详细介绍 MySQL 的优化技巧,帮助你在实际应用中提升数据库性能。 一、数据库设计优化 1. 数据库的规范化与反规

C++编程:ZeroMQ进程间(订阅-发布)通信配置优化

文章目录 0. 概述1. 发布者同步发送(pub)与订阅者异步接收(sub)示例代码可能的副作用: 2. 适度增加缓存和队列示例代码副作用: 3. 动态的IPC通道管理示例代码副作用: 4. 接收消息的超时设置示例代码副作用: 5. 增加I/O线程数量示例代码副作用: 6. 异步消息发送(使用`dontwait`标志)示例代码副作用: 7. 其他可以考虑的优化项7.1 立即发送(ZMQ_IM