【昕宝爸爸小模块】深入浅出之针对大Excel做文件读取问题

在这里插入图片描述


➡️博客首页       https://blog.csdn.net/Java_Yangxiaoyuan


       欢迎优秀的你👍点赞、🗂️收藏、加❤️关注哦。


       本文章CSDN首发,欢迎转载,要注明出处哦!


       先感谢优秀的你能认真的看完本文,有问题欢迎评论区交流,都会认真回复!


✅如何针对大Excel做文件读取?

  • 一、✅如何针对大Excel做文件读取
    • 1.1🟩XSSFWorkbook文件读取
    • 1.2🟩EasyExcel文件读取
  • 二、✅扩展知识
    • 2.1🟩 EasyExcel简介
    • 2.2🟩EasyExcel 为什么内存占用小?


一、✅如何针对大Excel做文件读取


在POI中,提供了SXSSFWorkbook,通过将部分数据写入磁盘上的临时文件来减少内存占用。但是SXSSFWorkbook只能用于文件写入,但是文件读取还是不行的,就像我们前面分析过的,Excel的文件读取还是会存在内存溢出的问题的。


🟢参考本人博客文件处理专栏: 什么是POI,为什么他会导致内存溢出?


🟢参考本人博客文件处理专栏: POI如何做大文件的写入?


那如果要解决这个问题,可以考虑使用EasyExcel。


EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。他能让你在不用考虑性能、
内存的等因素的情况下,快速完成Excel的读、写等功能。

关于使用XSSFWorkbook和EasyExcel的文件读取,我这里也做了个内存占用的对比:


1.1🟩XSSFWorkbook文件读取


读取一个27.3MB的文件(文件的生成代码📑✅链接: POI如何做大文件的写入)


package excel.read;import org.apache.poi.ss .usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @author xinbaobaba
* XSSFWorkbook文件读取代码示范
*/
public class XSSFExcelReadTest {public static void main(String[] args) {// 指定要读取的文件路径String filename = "example.xlsx";try (FileInputStream fileInputStream = new FileInputStream(new File(filename))) {// 创建工作簿对象Workbook workbook = new XSSFWorkbook(fileInputStream);//获取第一个工作表Sheet sheet = workbook.getSheetAt(0);// foreach 遍历所有行for (Row row : sheet) {// 遍历所有单元格for (Cell cell : row) {// 根据不同数据类型处理数据switch (cel1.getCel1Type()) {case STRING:System.out.print(cell.getstringCellValue() + " t");break;case NUMERIC:if (DateUtil.isCellDateFormatted(cell)) {System.out.print(cell.getDateCellValue() + "t");} else {System.out.print(cell.getNumericCellValue() + " t”);}break;case BOOLEAN:System.out.print(cell.getBooleanCellValue() + " t");break;case FORMULA:System.out.print(cell.getCellFormula() + "t");break;default:System.out.print("");}}System.out.println();//换行}}catch (IOException e)  {e.printStackTrace();}}
}

同样使用Arthas查看内存占用情况:


在这里插入图片描述


占用内存1000+M


1.2🟩EasyExcel文件读取


package excel.read;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener:/**
* @author xinbaobaba
* EasyExcel文件读取代码示例
*/
public class EasyExcelReadTest {public static void main(Stringl] args) {// 指定要读取的文件路径String filename = "example.xlsx";EasyExcel.read(filename, new PrintDataListener()).sheet().doRead();}
}// 监听器,用于处理读取到的数据
class PrintDatalistener implements ReadListener<Object> {@Overridepublic void invoke(Object data, AnalysisContext context) {//处理每一行的数据System.out.println(data) ;}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 所有数据解析完成后的操作}@Overridepublic void onException(Exception exception, AnalysisContext context) throws Exception {//处理读取过程中的异常}
}

同样使用Arthas查看内存占用情况:


在这里插入图片描述


内存占用只有不到100MB。


二、✅扩展知识


2.1🟩 EasyExcel简介


EasyExcel是一款软件程序,允许用户创建、编辑和分析电子表格。它设计成用户友好和直观,使得所有技能水平的用户都能轻松处理数据。EasyExcel提供了广泛的功能,包括进行计算、创建图表和图形以及在表格中组织数据的能力。它还支持各种文件格式,如.xls和.xlsx,使用户能够方便地导入和导出来自其他应用程序的数据。总体而言,EasyExcel是一种多功能的工具,用于管理和操作电子表格格式的数据。


2.2🟩EasyExcel 为什么内存占用小?


EasyExcel是一款基于POI(Apache开源的Java类库)开发的Excel操作工具。相比于传统的操作Excel的方式,EasyExcel采用了一种新的处理方式,即将Excel数据转化为对象列表然后进行操作。这种方式减少了对内存的占用,提高了数据处理的效率。


具体的来说,EasyExcel在内存占用方面有以下几个优势:


1. 逐行读写:EasyExcel通过逐行读写的方式操作Excel,即一次只读取或写入一行数据,而不是一次性读取或写入整个文件。这样可以大大减少对内存的占用。

2. 分段读写:当需要处理大文件时,EasyExcel可以将文件拆分成多个小段进行读写,每次只处理一小段数据,将读写的内存压力均匀分散,降低了内存的占用。

3. 内存缓冲区:EasyExcel内部使用了内存缓冲区来存储读取或写入的数据,通过合理控制缓冲区的大小,可以有效地减少对内存的占用。

4. 高效的数据处理算法:EasyExcel内部采用了高效的数据处理算法,例如使用零拷贝技术来提高数据读写的速度,减少对内存的占用。

综上所述,EasyExcel通过优化读写方式、使用内存缓冲区和高效的数据处理算法等手段,可以实现在相同数据量的情况下占用更小的内存空间,提高数据处理的效率。


参考代码示例:


import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.ReadListener;import java.io.File;/**
* @author xinbaobaba
* 展示了如何使用EasyExcel读取Excel文件并计算内存占用
*/
public class EasyExcelMemoryDemo {public static void main(String[] args) {String filePath = "path/to/excel/file.xlsx";// 创建一个监听器,用于统计内存占用ReadListener<Object> listener = new ReadListener<>() {private long startMemory;@Overridepublic void invoke(Object data, AnalysisContext context) {if (context.readRowHolder().getRowIndex() == 0) {// 记录读取第一行数据时的内存占用startMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 读取完所有数据后,计算内存占用的差值long endMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();long memoryUsage = endMemory - startMemory;System.out.println("Excel文件读取完毕");System.out.println("内存占用差值:" + memoryUsage + " bytes");}};// 读取Excel文件EasyExcel.read(new File(filePath)).registerReadListener(listener).sheet().doRead();}
}

输出Excel文件读取完毕后的内存占用差值。相比于使用传统的POI工具,使用EasyExcel读取Excel文件时,内存占用会更小。


EasyExcel是一个Java库,用于简化Excel文件的读写操作。相较于Apache POI等传统Excel处理库,EasyExcel具有内存占用小的优势。这主要归功于EasyExcel的底层实现和数据处理方式。


展示:


import com.alibaba.excel.EasyExcel;  
import com.alibaba.excel.ExcelReader;  
import com.alibaba.excel.ExcelWriter;  
import com.alibaba.excel.context.AnalysisContext;  
import com.alibaba.excel.event.AnalysisEventListener;  
import com.alibaba.excel.metadata.BaseRowModel;  
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;  
import com.alibaba.excel.write.metadata.WriteSheet;  
import com.alibaba.excel.write.metadata.WriteTable;  
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;  import java.util.ArrayList;  
import java.util.List;  /***@author 昕宝爸爸*@date 24/01/18*/
public class AdvancedMemoryOptimizedExample {  public static void main(String[] args) {  String inputFileName = "input_example_complex.xlsx";  String outputFileName = "output_example_complex.xlsx";  int pageSize = 1000;  int sheetCount = 3; // 定义要写入的Sheet数量  int totalPage = 0; // 总页数变量,用于动态计算总页数  // 创建数据模型类,用于存储读取到的数据  class DataModel extends BaseRowModel {  private String field1; // 字段1  private int field2; // 字段2  // 其他字段...  // 对应的getter和setter方法...  }  // 内存优化示例:读取Excel文件并分页处理  List<DataModel> dataList = new ArrayList<>(); // 创建一个空的列表用于存储数据  ExcelReader excelReader = EasyExcel.read(inputFileName, new NoModelDataListener<DataModel>()).build(); // 使用自定义的DataModel类作为监听器的参数类型  ReadSheet readSheet = EasyExcel.readSheet(pageSize).build(); // 设置读取时的分页参数  excelReader.read(readSheet, new AnalysisEventListener<DataModel>() { // 使用自定义的DataModel类作为事件处理接口的参数类型  @Override  public void invoke(DataModel data, AnalysisContext context) {  // 在invoke方法中进行自定义数据处理逻辑,比如将读取到的数据存储到数据库等操作...  dataList.add(data); // 这里仅作示例,将读取到的数据添加到列表中,实际应用中可能需要进行其他处理  }  @Override  public void doAfterAllAnalysed(AnalysisContext context) {  // 处理读取完所有数据后的逻辑...可以在这里添加一些操作,比如关闭资源等  totalPage = context.getHead().getSheet().getVirtual(context).getTotalRowNum() / pageSize + 1; // 动态计算总页数,用于后续写入操作时使用  }  }); // 开始读取数据,并传入监听器进行数据处理  excelReader.finish(); // 读取完成后关闭Excel读取器  // 内存优化示例:写入Excel文件并自定义样式和分页写入策略  WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); // 创建一个自定义的单元格样式对象,可以根据需要设置样式属性  HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(null, contentWriteCellStyle); // 创建一个水平单元格样式策略对象,用于设置单元格样式策略  ExcelWriterBuilder writerBuilder = EasyExcel.write(outputFileName); // 创建Excel写入器构建器对象,用于配置写入参数和设置样式策略等  writerBuilder.registerWriteHandler(horizontalCellStyleStrategy); // 注册之前创建的样式策略对象到写入器中,用于设置单元格样式策略  ExcelWriter excelWriter = writerBuilder.build(); // 创建Excel写入器实例,用于实际写入操作  for (int i = 1; i <= sheetCount; i++) { // 根据定义的Sheet数量循环创建多个Sheet进行写入操作  WriteSheet writeSheet = EasyExcel.writerSheet("Sheet" + i).build(); // 创建多个Sheet进行写入操作,每个Sheet名称分别为"Sheet1"、"Sheet2"等,可以根据需要自定义Sheet名称和数量等参数  excelWriter.write(dataList, writeSheet, totalPage); // 将之前读取到的数据写入到输出文件中,并传入Sheet参数和样式策略等参数,同时传入动态计算的总页数参数,用于分页写入操作时使用  } // 循环写入多个Sheet数据到输出文件中,并传入对应的样式策略和总页数参数等

主要通过以下几个方式来节省内存:


使用分页读取和写入:通过设置pageSize参数,将数据分页读取和写入,可以减少一次性读取和写入的数据量,从而减少内存占用。


使用数据模型类:通过创建一个数据模型类(DataModel),将读取到的数据存储到该类的对象中,而不是直接存储到原始数据类型列表中。这样可以避免为每个数据项创建过多的对象,从而减少内存占用。


使用水平单元格样式策略:通过创建一个水平单元格样式策略对象(HorizontalCellStyleStrategy),并将样式策略注册到Excel写入器中。该策略可以根据单元格的内容自动应用样式,从而减少手动设置样式所带来的内存开销。


动态计算总页数:在读取完所有数据后,通过调用context.getHead().getSheet().getVirtual(context).getTotalRowNum()方法,动态计算总页数。这样可以避免在循环中多次计算总页数,从而减少内存占用。


综上所述,通过分页读取和写入、使用数据模型类、使用水平单元格样式策略以及动态计算总页数等方式,实现了内存的优化使用

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

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

相关文章

vue:处理base64格式文件pdf、图片预览

一、需求&#xff1a;后端返回是base64数据&#xff0c;需要前端处理来展示文件。 二、实现方法&#xff1a; 解释一下这段代码的功能&#xff1a; &#xff09;preview(item) 是一个函数&#xff0c;接受一个参数 item&#xff0c;其中包含了文件的相关信息。 &#xff09;首…

rust跟我学五:是否安装双系统

图为RUST吉祥物 大家好,我是get_local_info作者带剑书生,这里用一篇文章讲解get_local_info是怎么得到检测双系统的。 首先,先要了解get_local_info是什么? get_local_info是一个获取linux系统信息的rust三方库,并提供一些常用功能,目前版本0.2.4。详细介绍地址:[我的Ru…

npm超详细安装(包括配置环境变量)!!!npm安装教程(node.js安装教程)

安装node.js:(建议选择相对低一点的版本&#xff0c;相对稳定)​下载完成直接点击next即可(安装过程中会直接添加path的系统变量&#xff0c;变量值是自己的安装路径&#xff0c;可自行选择&#xff0c;比如&#xff1a;D:\software\)​安装完成:winR打开电脑控制台&#xff0c…

边缘计算的挑战和机遇

目录 前言 一、边缘计算 &#xff08;一&#xff09;为什么需要边缘计算 &#xff08;二&#xff09;什么是边缘计算 &#xff08;三&#xff09;边缘计算体系架构 &#xff08;四&#xff09;边缘计算的好处 二、案例分析 &#xff08;一&#xff09;云卸载 &#xf…

Linux下如何快速调试I2C设备

Linux下如何快速调试I2C设备 目录 1 什么场景下需要快速调试I2C设备 2 如何快速调试I2C设备 3 如何获取I2C Tools工具集 3.1 获取I2C Tools工具集源码 3.2 编译I2C Tools工具集源码 3.3 为设备添加I2C Tools工具集 4 如何使用I2C Tools工具集 5 小结 1 什么场景下需要快…

1127: 矩阵乘积

题目描述 计算两个矩阵A和B的乘积。 输入 第一行三个正整数m、p和n&#xff0c;0<m,n,p<10&#xff0c;表示矩阵A是m行p列&#xff0c;矩阵B是p行n列&#xff1b; 接下来的m行是矩阵A的内容&#xff0c;每行p个整数&#xff0c;用空格隔开&#xff1b; 最后的p行是矩…

操作系统课程设计-内存管理

目录 前言 1 实验题目 2 实验目的 3 实验内容 3.1 步骤 3.2 关键代码 3.2.1 显示虚拟内存的基本信息 3.2.2 遍历当前进程的虚拟内存 4 实验结果与分析 5 代码 前言 本实验为课设内容&#xff0c;博客内容为部分报告内容&#xff0c;仅为大家提供参考&#xff0c;请勿直…

#LLMOps##AIGC# Dify_构建本地知识库问答应用-生成Al应用的创新引擎 用于构建助手API和GPT的开源开发平台

github&#xff1a; https://github.com/langgenius/dify/blob/main/README_CN.md 介绍文档&#xff1a;https://docs.dify.ai/getting-started/readme Dify 介绍 Dify 笔记 Dify 是什么&#xff1f; 开源的大语言模型&#xff08;LLM&#xff09;应用开发平台融合了后端即服…

“中国最危险的女人”闪亮现身瑞士

媒体报道&#xff0c;昨&#xff08;1月17日&#xff09;天中午&#xff0c;财新CEO午餐会在瑞士达沃斯举办&#xff0c;财新传媒社长胡舒立女士在致辞&#xff0c;她的现身&#xff0c;进一步粉碎了不久前“胡舒立被国安约谈”的网传谣言。 图&#xff1a;财新传媒社长胡舒立&…

Docker部署的gitlab升级指南(15.11.X容器里升级PostgreSQL到13.8)

一、确定当前版本 #进入当前版本容器产看gitlab版本 docker exec -it gitlab cat /opt/gitlab/embedded/service/gitlab-rails/VERSION#显示版本如下 14.4.0二、备份数据&#xff0c;防止升级发生意外 #执行备份命令 docker exec -ti gitlab gitlab-rake gitlab:backup:creat…

【K8S 云原生】K8S的对外服务—ingress

目录 一、K8S的Service 1、Service的作用 2、Service类型&#xff1a; 二、ingress 1、ingress的组成&#xff1a; 2、ingress资源的定义项&#xff1a; 3、ingress暴露服务端的方式 3.1、DeploymentLoadBalancer模式&#xff1a; 1、工作流程图&#xff1a; 3.2、Dae…

软件工程:黑盒测试等价分类法相关知识和多实例分析

目录 一、黑盒测试和等价分类法 1. 黑盒测试 2. 等价分类法 二、黑盒测试等价分类法实例分析 1. 工厂招工年龄测试 2. 规定电话号码测试 3. 八位微机测试 4. 三角形判断测试 一、黑盒测试和等价分类法 1. 黑盒测试 黑盒测试就是根据被测试程序功能来进行测试&#xf…