- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
使用spring boot 对excel 进行操作在平时项目中要经常使用。常见通过jxl和poi 的方式进行操作。但他们都存在一个严重的问题就是非常的耗内存。这里介绍一种 Easy Excel 工具来对excel进行操作.
EasyExcel 是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。easyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析.
从excel 中读取数据,常用的场景就是读取excel的数据,将相应的数据保存到数据库中。需要实现一定的逻辑处理.
< dependency > < groupId > com.alibaba </ groupId > < artifactId > easyexcel </ artifactId > < version > 2.2.10 </ version > </ dependency >
@Data public class User { @ExcelProperty(index = 0 ) private Integer id; @ExcelProperty(index = 1 ) private String name; @ExcelProperty(index = 2 ) private Integer age; }
比如我们要读取两列的数据,就写两个属性。@ExcelProperty(index = 0)来设置要读取的列,index=0表示读取第一列.
监听器继承 AnalysisEventListener 类 。
@Slf4j public class UserExcelListener extends AnalysisEventListener<User> { /** * 解析excel文档的每一行 * @param user 参数user即是每行读取数据转换的User对象 * @param analysisContext */ @Override public void invoke(User user, AnalysisContext analysisContext){ log.info( "excel数据行:{}" ,user.toString()); } /** * 整个文档解析完执行 * @param analysisContext */ @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { log.info( "文档解析完毕" ); } }
当解析每一条数据时都会调用invoke方法,当所有数据都解析完毕时最后会调用doAfterAllAnalysed方法。可以在监听类内的方法中将每次读取到的数据进行保存或者其他操作处理.
/** * 上传excel文件并读取其中内容 * * @param file * @return */ @PostMapping( "/upload" ) public String uploadExcel(MultipartFile file) { log.info( "easyExcel上传文件:{}" , file); try { InputStream inputStream = file.getInputStream(); EasyExcel.read(inputStream, User. class , new UserExcelListener() ) .sheet() .doRead(); } catch (Exception e) { } return "表格文件上传成功" ; }
写操作有两种写法,一种是不创建对象的写入,另一种是根据对象写入。这里主要介绍创建对象写入 。
@Data public class User { @ExcelProperty(index = 0 ) private Integer id; @ExcelProperty(index = 1 ) private String name; @ExcelProperty(index = 2 ) private Integer age; }
注意@ExcelProperty(“用户编号”) 会生成相应的列名为 用户编号,如果不设置,则会直接将字段名设置为excel的列名.
/** * 输出导出excel */ @PostMapping( "/export" ) public void export() { ArrayList <User> users = new ArrayList<> (); for ( int i = 0; i < 10; i++ ) { User user = new User(); user.setId(i); user.setName( "测试用户-" + i); user.setAge( 20 + i); users.add(user); } log.info( "导出数据结果集:{}" , users); String fileName = "C:\\Users\\pytho\\Desktop\\fsdownload\\用户信息表.xlsx" ; EasyExcel.write(fileName, User. class ) .autoCloseStream( true ) .sheet( "sheet名称" ) .doWrite(users); }
/** * 输出导出excel */ @PostMapping( "/export1" ) public void export1() { ArrayList <User> users = new ArrayList<> (); for ( int i = 0; i < 10; i++ ) { User user = new User(); user.setId(i); if (i == 3 || i == 4 || i == 5 ) { user.setName( "测试用户-3" ); } else { user.setName( "测试用户-" + i); } user.setAge( 20 + i); users.add(user); } log.info( "导出数据结果集:{}" , users); String fileName = "C:\\Users\\pytho\\Desktop\\fsdownload\\(单列相同内容合并单元格)用户信息表.xlsx" ; EasyExcel.write(fileName, User. class ) .registerWriteHandler( new SimpleExcelMergeUtil() ) .autoCloseStream( true ) .sheet( "sheet名称" ) .doWrite(users); }
如果要对导出的excel进行处理,就需要自定义处理器类进行处理 。
自定义easyExcel处理器(单列合并:根据用户id相同的列进行合并单元格):
/** * @version 1.0 * @Package: com.stech.bms.buss.utils * @ClassName: ExcelMergeUtil * @Author: sgq * @Date: 2023/7/28 13:29 * @Description: 仅处理单列数据相同合并单元格 */ public class SimpleExcelMergeUtil implements CellWriteHandler { public SimpleExcelMergeUtil() { } /** * 创建每个单元格之前执行 * * @param writeSheetHolder * @param writeTableHolder * @param row * @param head * @param columnIndex * @param relativeRowIndex * @param isHead */ @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { } /** * 创建每个单元格之后执行 * * @param writeSheetHolder * @param writeTableHolder * @param cell * @param head * @param relativeRowIndex * @param isHead */ @Override public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { } /** * 每个单元格数据内容渲染之后执行 * * @param writeSheetHolder * @param writeTableHolder * @param cellData * @param cell * @param head * @param relativeRowIndex * @param isHead */ @Override public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { } /** * 每个单元格完全创建完之后执行 * * @param writeSheetHolder * @param writeTableHolder * @param cellDataList * @param cell * @param head * @param relativeRowIndex * @param isHead */ @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 当前行 int curRowIndex = cell.getRowIndex(); // 当前列 int curColIndex = cell.getColumnIndex(); if (! isHead) { if (curRowIndex > 1 && curColIndex == 1 ) { // 从第二行数据行开始,获取当前行第二列数据 Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue(); // 获取上一行第二列数据 Cell preCell = cell.getSheet().getRow(curRowIndex - 1 ).getCell(curColIndex); Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue(); if (curData.equals(preData)) { Sheet sheet = writeSheetHolder.getSheet(); List <CellRangeAddress> mergedRegions = sheet.getMergedRegions(); boolean isMerged = false ; for ( int i = 0; i < mergedRegions.size() && !isMerged; i++ ) { CellRangeAddress cellRangeAddr = mergedRegions.get(i); // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元 if (cellRangeAddr.isInRange(curRowIndex - 1 , curColIndex)) { sheet.removeMergedRegion(i); cellRangeAddr.setLastRow(curRowIndex); sheet.addMergedRegion(cellRangeAddr); isMerged = true ; } } // 若上一个单元格未被合并,则新增合并单元 if (! isMerged) { CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1 , curRowIndex, curColIndex, curColIndex); sheet.addMergedRegion(cellRangeAddress); } } } } } }
/** * 输出导出excel */ @PostMapping( "/export2" ) public void export2() { ArrayList <User> users = new ArrayList<> (); for ( int i = 0; i < 10; i++ ) { User user = new User(); user.setId(i); if (i == 3 || i == 4 || i == 5 ) { user.setName( "测试用户-3" ); } else { user.setName( "测试用户-" + i); } user.setAge( 20 + i); users.add(user); } log.info( "导出数据结果集:{}" , users); // 从第几行开始合并 int mergeStartRowIndex = 5 ; // 需要合并哪些列 int [] mergeColumns = {1 }; String fileName = "C:\\Users\\pytho\\Desktop\\fsdownload\\(单列相同内容合并单元格-通用版)用户信息表.xlsx" ; EasyExcel.write(fileName, User. class ) .registerWriteHandler( new SimpleCommonExcelMergeUtil(mergeStartRowIndex,mergeColumns)) .autoCloseStream( true ) .sheet( "sheet名称" ) .doWrite(users); }
excel处理器类:
/** * @version 1.0 * @Package: com.stech.bms.buss.utils * @ClassName: ExcelMergeUtil * @Author: sgq * @Date: 2023/7/28 13:29 * @Description: 仅处理单列数据相同合并单元格 */ public class SimpleCommonExcelMergeUtil implements CellWriteHandler { private int mergeStartRowIndex; private int [] mergeColumns; private List<Integer> mergeColumnList; public SimpleCommonExcelMergeUtil() { } public SimpleCommonExcelMergeUtil( int mergeStartRowIndex, int [] mergeColumns) { this .mergeStartRowIndex = mergeStartRowIndex; this .mergeColumns = mergeColumns; mergeColumnList = new ArrayList<> (); for ( int i : mergeColumns) { mergeColumnList.add(i); } } /** * 创建每个单元格之前执行 * * @param writeSheetHolder * @param writeTableHolder * @param row * @param head * @param columnIndex * @param relativeRowIndex * @param isHead */ @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { } /** * 创建每个单元格之后执行 * * @param writeSheetHolder * @param writeTableHolder * @param cell * @param head * @param relativeRowIndex * @param isHead */ @Override public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { } /** * 每个单元格数据内容渲染之后执行 * * @param writeSheetHolder * @param writeTableHolder * @param cellData * @param cell * @param head * @param relativeRowIndex * @param isHead */ @Override public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { } /** * 每个单元格完全创建完之后执行 * * @param writeSheetHolder * @param writeTableHolder * @param cellDataList * @param cell * @param head * @param relativeRowIndex * @param isHead */ @Override public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 当前行 int curRowIndex = cell.getRowIndex(); // 当前列 int curColIndex = cell.getColumnIndex(); if (! isHead) { if (curRowIndex > mergeStartRowIndex && mergeColumnList.contains(curColIndex)) { // 从第二行数据行开始,获取当前行第二列数据 Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue(); // 获取上一行第二列数据 Cell preCell = cell.getSheet().getRow(curRowIndex - 1 ).getCell(curColIndex); Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue(); if (curData.equals(preData)) { Sheet sheet = writeSheetHolder.getSheet(); List <CellRangeAddress> mergedRegions = sheet.getMergedRegions(); boolean isMerged = false ; for ( int i = 0; i < mergedRegions.size() && !isMerged; i++ ) { CellRangeAddress cellRangeAddr = mergedRegions.get(i); // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元 if (cellRangeAddr.isInRange(curRowIndex - 1 , curColIndex)) { sheet.removeMergedRegion(i); cellRangeAddr.setLastRow(curRowIndex); sheet.addMergedRegion(cellRangeAddr); isMerged = true ; } } // 若上一个单元格未被合并,则新增合并单元 if (! isMerged) { CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1 , curRowIndex, curColIndex, curColIndex); sheet.addMergedRegion(cellRangeAddress); } } } } } }
这只是简单的合并单元格例子,抛砖引玉的作用。工作中可能会遇到很多情况:合并单元格后第一列序列号也需要根据其他列进行合并单元格且序列号还必须保持连续,根据部分列合并单元格,隔行合并单元格等等情况,这就需要开发者对easyExcel的处理器类里面的api比较了解才能完成。遇到的问题也可以留言,看到也会尝试一起处理解决.
。
最后此篇关于[EasyExcel]导出合并单元格的文章就讲到这里了,如果你想了解更多关于[EasyExcel]导出合并单元格的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我试图要求 test/unit 的新版本(即不是与 ruby 捆绑的版本) .根据 instructions我用 gem i test-unit 安装它.但是现在当我需要 test/unit我似乎
简短版本是: 我有一个 systemd 单元,我想在调用时检查脚本的返回代码: systemctl status service.service 长版本:我有一个 lsb init 脚本正是这样做的,
我正在使用反射动态创建一个类的实例,这工作正常,除非尝试通过单元测试执行此操作 - 我使用的是 MS 测试框架。 我收到熟悉的错误:“无法加载文件或程序集‘Assy’或其依赖项之一。系统找不到指定的文
我想知道测试网站“界面功能”的最佳实践是什么。 我对哪些方法可用于测试界面和动态生成的内容感兴趣。特别是,我很难弄清楚是否可以为需要用户交互的操作创建自动化测试,或者这是否只是浪费时间,我应该让一些
我有一个简单的 Python 服务,其中有一个无限执行某些操作的循环。在各种信号上,sys.exit(0) 被调用,这导致 SystemExit 被引发,然后如果可以的话应该进行一些清理。 在测试中,
我正在使用 OpenCV 2.4.2 这是 OpenCV 文档中的引文 C++: void HoughLinesP(InputArray image, OutputArray lines, doubl
忙于 C# 中的自动化测试用例,我们需要在每次测试后恢复数据库快照。问题是,当运行多个测试时它会失败,因为“其他用户正在使用数据库时无法更改数据库状态。” 我们使用 SqlConnection.Cle
我阅读了 C# 规范并用谷歌搜索了它,但一无所获。 我 99% 肯定 C# 中没有像单元命名空间指令这样的功能,但问题是:为什么?是否有惯用或技术原因? 这很方便,尤其是当我们的大部分文件都由单个命名
我目前正在尝试向我的应用程序(一个非常老的项目......评论说 iOS 2.0)添加单元测试(精确的应用程序测试)并且总是偶然发现 undefined symbols for architectur
我正在使用Delphi 7,并且有一个新单元要在我的项目中使用。我已经编译了新的。当我尝试通过将其添加到uses子句在项目中使用此单元时,出现错误,提示未找到.dcu文件。我还尝试将文件的完整路径放在
场景:我需要编写一个复杂的nHibernate查询,该查询将返回预计的DTO,但是我想使用TDD方法。该方法如下所示: public PrintDTO GetUsersForPrinting(int
您可以通过运行以下命令在事件 html 设置中显示 Jupyter 笔记本: $ jupyter nbconvert untitled.ipynb --to slides --post serve 有
如何在一个网站上拥有多个 AdSense 单元? Google 提供的唯一代码是按单位计算的。 (adsbygoogle = window.adsbygoogle || []).push({})
我刚刚开始为大量代码编写测试。有很多类依赖于文件系统,即读取 CSV 文件、读/写配置文件等。 当前测试文件存储在项目(这是一个 Maven2 项目)的 test 目录中,但由于多种原因该目录并不总是
我对 TDD 还很陌生,在单元测试方面也不是很老练,所以才有这个问题。我有这个用 PHP 编写的遗留函数 function foo(){ x = bar(); y = baz();
我创建了一个程序,在 Swing 窗口的一侧显示结果过滤选项,但默认情况下它们水平相邻显示,这浪费了我在 BorderLayout 的西侧分配的空间。我可以在构造函数或添加语句中传递任何内容来将它们堆
标题不好的借口:如果有人能更好地描述它,请做。 我有一个 WeakList类,它“基本上”是一个 List> (虽然不是字面意义上的派生自列表,但它应该对用户完全透明)。 现在的基本思想是“如果引用的
我正在尝试在 UITableView 上添加两个原型(prototype)单元。但是,我不知道如何验证是否能够为每个原型(prototype)“返回”正确的单元格。你们能帮我一下吗? func ta
我正在使用 CloudKit 作为数据库创建一个简单的待办事项列表应用程序。目前我可以添加和删除对象,但对编辑对象感到困惑。 编辑项目 Controller protocol EditItemCont
我正在针对以下任务训练 RNN:给定一个包含 30 个单词的序列,然后将该序列分类为二进制类。 在我的网络中拥有超过 30 个单元(LSTM、GRU 或普通 RNN)有好处吗?我在网上看到过很多例子,
我是一名优秀的程序员,十分优秀!