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

相关文章

MySQL中慢SQL优化方法的完整指南

《MySQL中慢SQL优化方法的完整指南》当数据库响应时间超过500ms时,系统将面临三大灾难链式反应,所以本文将为大家介绍一下MySQL中慢SQL优化的常用方法,有需要的小伙伴可以了解下... 目录一、慢SQL的致命影响二、精准定位问题SQL1. 启用慢查询日志2. 诊断黄金三件套三、六大核心优化方案方案

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

Java利用poi实现word表格转excel

《Java利用poi实现word表格转excel》这篇文章主要为大家详细介绍了Java如何利用poi实现word表格转excel,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、每行对象类需要针对不同的表格进行对应的创建。package org.example.wordToEx

使用国内镜像源优化pip install下载的方法步骤

《使用国内镜像源优化pipinstall下载的方法步骤》在Python开发中,pip是一个不可或缺的工具,用于安装和管理Python包,然而,由于默认的PyPI服务器位于国外,国内用户在安装依赖时可... 目录引言1. 为什么需要国内镜像源?2. 常用的国内镜像源3. 临时使用国内镜像源4. 永久配置国内镜

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

Java嵌套for循环优化方案分享

《Java嵌套for循环优化方案分享》介绍了Java中嵌套for循环的优化方法,包括减少循环次数、合并循环、使用更高效的数据结构、并行处理、预处理和缓存、算法优化、尽量减少对象创建以及本地变量优化,通... 目录Java 嵌套 for 循环优化方案1. 减少循环次数2. 合并循环3. 使用更高效的数据结构4

使用Apache POI在Java中实现Excel单元格的合并

《使用ApachePOI在Java中实现Excel单元格的合并》在日常工作中,Excel是一个不可或缺的工具,尤其是在处理大量数据时,本文将介绍如何使用ApachePOI库在Java中实现Excel... 目录工具类介绍工具类代码调用示例依赖配置总结在日常工作中,Excel 是一个不可或缺的工http://

Java实战之利用POI生成Excel图表

《Java实战之利用POI生成Excel图表》ApachePOI是Java生态中处理Office文档的核心工具,这篇文章主要为大家详细介绍了如何在Excel中创建折线图,柱状图,饼图等常见图表,需要的... 目录一、环境配置与依赖管理二、数据源准备与工作表构建三、图表生成核心步骤1. 折线图(Line Ch

Deepseek使用指南与提问优化策略方式

《Deepseek使用指南与提问优化策略方式》本文介绍了DeepSeek语义搜索引擎的核心功能、集成方法及优化提问策略,通过自然语言处理和机器学习提供精准搜索结果,适用于智能客服、知识库检索等领域... 目录序言1. DeepSeek 概述2. DeepSeek 的集成与使用2.1 DeepSeek API

Tomcat高效部署与性能优化方式

《Tomcat高效部署与性能优化方式》本文介绍了如何高效部署Tomcat并进行性能优化,以确保Web应用的稳定运行和高效响应,高效部署包括环境准备、安装Tomcat、配置Tomcat、部署应用和启动T... 目录Tomcat高效部署与性能优化一、引言二、Tomcat高效部署三、Tomcat性能优化总结Tom