springboot使用EasyExcel实现Excel导入导出

news/2024/7/27 8:23:35/文章来源:https://blog.csdn.net/qq_41482600/article/details/136562476

         java生成Excel比较有名的框架有Apache poi、jxl等,但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。

        EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部载到内存中,而是从磁盘上一行行读取数据,逐个解析。EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理( AnalysisEventListener )。

1、pom添加easyexcel依赖

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.3</version>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.18</version>
</dependency>

2、实体类UserDO与Excel数据对应

@Data
public class UserDO {// 设置excel表头名称@ExcelProperty("用户编号")@ColumnWidth(20)private Long id;// 设置该列的名称为'用户名';@ExcelProperty("用户名")// 设置表格列的宽度为20@ColumnWidth(20)private String username;// 导出时忽略该字段@ExcelIgnoreprivate String password;@ExcelProperty("昵称")@ColumnWidth(20)private String nickname;@ExcelProperty("生日")@ColumnWidth(20)// 按照指定的格式对日期进行格式化;@DateTimeFormat("yyyy-MM-dd")private Date birthday;@ExcelProperty("手机号")@ColumnWidth(20)private String phone;@ExcelProperty("身高(米)")@NumberFormat("#.##")@ColumnWidth(20)private Double height;// 自定义内容转换器@ExcelProperty(value = "性别", converter = GenderConverter.class)@ColumnWidth(10)private Integer gender;}

常用注解有:
@ExcelProperty 指定当前字段对应excel中的哪一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。
@ExcelIgnore EasyExcel默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
@DateTimeFormat 日期转换,用String去接收excel日期格式的数据会调用这个注解。里面的value参照java.text.SimpleDateFormat
@NumberFormat 数字转换,用String去接收excel数字格式的数据会调用这个注解。里面的value参照java.text.DecimalFormat

3、converter自定义转换器

自定义转换器将数据库中表示性别的1、0转换成男、女

  3.1、GenderConverter 

/*** 性别转换器* */
public class GenderConverter implements Converter<Integer> {@Overridepublic Class<?> supportJavaTypeKey() {// 实体类中对象属性类型return Integer.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {// Excel中对应的CellData(单元格数据)属性类型return CellDataTypeEnum.STRING;}/*** 将单元格里的数据转为java对象,也就是女转成2,男转成1,用于导入excel时对性别字段进行转换* */@Overridepublic Integer convertToJavaData(ReadConverterContext<?> context) throws Exception {// 从CellData中读取数据,判断Excel中的值,将其转换为预期的数值return GenderEnum.convert(context.getReadCellData().getStringValue()).getValue();}/*** 将java对象转为单元格数据,也就是2转成女,1转成男,用于导出excel时对性别字段进行转换* */@Overridepublic WriteCellData<?> convertToExcelData(WriteConverterContext<Integer> context) throws Exception {// 判断实体类中获取的值,转换为Excel预期的值,并封装为CellData对象return new WriteCellData<>(GenderEnum.convert(context.getValue()).getDescription());}
}

 3.2、GenderEnum

@Getter
@AllArgsConstructor
public enum GenderEnum {/*** 未知*/UNKNOWN(0, "未知"),/*** 男性*/MALE(1, "男性"),/*** 女性*/FEMALE(2, "女性");private final Integer value;@JsonFormatprivate final String description;public static GenderEnum convert(Integer value) {
//        用于为给定元素创建顺序流
//        values:获取枚举类型的对象数组return Stream.of(values()).filter(bean -> bean.value.equals(value)).findAny().orElse(UNKNOWN);}public static GenderEnum convert(String description) {return Stream.of(values()).filter(bean -> bean.description.equals(description)).findAny().orElse(UNKNOWN);}}

4、导出

/*** 设置响应结果** @param response    响应结果对象* @param rawFileName 文件名* @throws UnsupportedEncodingException 不支持编码异常*/
private void setExcelResponseProp(HttpServletResponse response, String rawFileName) throws UnsupportedEncodingException {//设置内容类型response.setContentType("application/vnd.vnd.ms-excel");//设置编码格式response.setCharacterEncoding("utf-8");//设置导出文件名称(避免乱码)String fileName = URLEncoder.encode(rawFileName.concat(".xlsx"), "UTF-8");// 设置响应头response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
}private Date getBirthday(int year, int month, int day){Calendar calendar = Calendar.getInstance();calendar.set(year, month, day);return calendar.getTime();
}/*** 导出数据* */
@GetMapping("/export/user")
public void exportUserExcel(HttpServletResponse response) throws IOException {OutputStream outputStream = response.getOutputStream();try {this.setExcelResponseProp(response, "用户列表");// 模拟根据条件在数据库查询数据List<UserDO> userList = new ArrayList<>();for(int i = 0;i < 30;i++){UserDO userDO = new UserDO();userDO.setBirthday(getBirthday(2001,1,i));userDO.setGender(1);userDO.setHeight(Double.valueOf(i));userDO.setId(Long.valueOf(i));userDO.setPhone("138"+i);userDO.setNickname("yuanhaoz");userDO.setPassword("5849"+i);userDO.setUsername("monky"+i);userList.add(userDO);}//这个实现方式非常简单直接,使用EasyExcel的write方法将查询到的数据进行处理,以流的形式写出即可EasyExcel.write(outputStream,UserDO.class)//对应的导出实体类.excelType(ExcelTypeEnum.XLSX)//excel文件类型,包括CSV、XLS、XLSX.sheet("用户列表")//导出sheet页名称.doWrite(userList); //查询获取的数据集合List<T>,转成excel} catch (IOException e) {throw new RuntimeException(e);}finally {outputStream.flush();outputStream.close();}
}

5、多sheet导出

/*** 多sheet导出数据* */
@GetMapping("/manySheet")
public void exportManySheet(HttpServletResponse response)throws IOException{OutputStream outputStream=response.getOutputStream();ExcelWriter writer = EasyExcel.write(outputStream, UserDO.class).excelType(ExcelTypeEnum.XLSX).build();try {this.setExcelResponseProp(response, "用户列表");// 模拟根据条件在数据库分页查询数据for(int j = 1;j <= 5;j++){List<UserDO> userList = new ArrayList<>();for(int i = 0;i < 30;i++){UserDO userDO = new UserDO();userDO.setBirthday(getBirthday(2001,1,i));userDO.setGender(1);userDO.setHeight(Double.valueOf(i));userDO.setId(Long.valueOf(i));userDO.setPhone("138"+i);userDO.setNickname("yuanhaoz"+i);userDO.setPassword("5849"+i);userDO.setUsername("monky"+i);userList.add(userDO);System.out.println(i);}//创建新的sheet页WriteSheet writeSheet = EasyExcel.writerSheet("用户信息" + j).build();//将list集合中的对象写到对应的sheet中去writer.write(userList,writeSheet);}} catch (IOException e) {throw new RuntimeException(e);//给提示todo}finally {writer.finish();outputStream.flush();outputStream.close();}
}

6、导入

6.1、UserListener

/*** 自定义监听器,对下载的excel中的数据进行校验* */
public class UserListener extends AnalysisEventListener {List<String> names = new ArrayList<>();/*** 每解析一行,回调该方法** @param data* @param context*/@Overridepublic void invoke(Object data, AnalysisContext context) {//校验名称String name = ((UserDO) data).getUsername();if (StrUtil.isBlank(name)) {throw new RuntimeException(String.format("第%s行名称为空,请核实", context.readRowHolder().getRowIndex() + 1));}if (names.contains(name)) {throw new RuntimeException(String.format("第%s行名称已重复,请核实", context.readRowHolder().getRowIndex() + 1));} else {names.add(name);}}/*** 出现异常回调** @param exception* @param context* @throws Exception*/@Overridepublic void onException(Exception exception, AnalysisContext context) throws Exception {if (exception instanceof ExcelDataConvertException) {/**从0开始计算*/Integer columnIndex = ((ExcelDataConvertException) exception).getColumnIndex() + 1;Integer rowIndex = ((ExcelDataConvertException) exception).getRowIndex() + 1;String message = "第" + rowIndex + "行,第" + columnIndex + "列" + "数据格式有误,请核实";throw new RuntimeException(message);} else if (exception instanceof RuntimeException) {throw exception;} else {super.onException(exception, context);}}/*** 解析完,全部回调** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {//解析完,全部回调逻辑实现names.clear();}
}

6.2、ImportData

/*** 导入数据* */
@PostMapping(value = "/importData")
public void ImportData(MultipartFile file){try {//获取文件的输入流InputStream inputStream = file.getInputStream();List<UserDO> list = EasyExcel.read(inputStream) //调用read方法//注册自定义监听器,字段校验可以在监听器内实现.registerReadListener(new UserListener()).head(UserDO.class) //对应导入的实体类.sheet(0) //导入数据的sheet页编号,0代表第一个sheet页,如果不填,则会导入所有sheet页的数据.headRowNumber(1) //列表头行数,1代表列表头有1行,第二行开始为数据行.doReadSync(); //开始读Excel,返回一个List<T>集合,继续后续入库操作//模拟导入数据库操作for (UserDO userDO : list){System.out.println(userDO.toString());}} catch (IOException exception){throw new RuntimeException(exception);}
}

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

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

相关文章

C# OpenCvSharp DNN 部署L2CS-Net人脸朝向估计

目录 介绍 效果 模型信息 项目 代码 下载 介绍 github地址&#xff1a;https://github.com/Ahmednull/L2CS-Net The official PyTorch implementation of L2CS-Net for gaze estimation and tracking 效果 模型信息 Inputs ------------------------- name&#xff1…

深入探索加载器(Loader)与插件(Plugin)的工作原理与技术实现

在软件开发和构建过程中&#xff0c;加载器与插件是两大核心组件&#xff0c;它们共同助力开发者实现代码的模块化、可扩展性以及复用性。对于技术型论坛的读者来说&#xff0c;理解这两者的工作原理和技术实现至关重要。以下&#xff0c;我们将对加载器和插件进行深入的剖析。…

【大数据】通过 docker-compose 快速部署 MinIO 保姆级教程

文章目录 一、概述二、MinIO 与 Ceph 对比1&#xff09;架构设计对比2&#xff09;数据一致性对比3&#xff09;部署和管理对比4&#xff09;生态系统和兼容性对比 三、前期准备1&#xff09;部署 docker2&#xff09;部署 docker-compose 四、创建网络五、MinIO 编排部署1&…

sheng的学习笔记-AI-多分类学习:ECOC,softmax

目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 基本术语&#xff1a; 若我们欲预测的是离散值&#xff0c;例如“好瓜”“坏瓜”&#xff0c;此类学习任务称为“分类”(classification)&#xff1b; 若欲预测的是连续值&#xff0c;例如西瓜成熟度0.95、0.37&#xff0c;…

Java基础数据结构之队列

一.什么是队列 队列是一种先进先出的数据结构&#xff0c;也就是从左边进从右边出&#xff0c;或者说&#xff0c;只允许在一端插入元素&#xff0c;在另一端删除元素 进行插入操作的一端称为队尾&#xff08;tail/rear&#xff09;&#xff0c;删除操作的一段称为队头&#…

第二门课:改善深层神经网络<超参数调试、正则化及优化>-优化算法

文章目录 1 Mini-batch梯度下降2 理解Mini-batch梯度下降法3 指数加权平均数4 理解指数加权平均数5 指数加权平均的偏差修正7 RMSprop<均方根传播>8 Adam优化算法<Momentum与RMSprop结合>9 学习率衰减10 局部最优的问题 1 Mini-batch梯度下降 Batch梯度下降法&…

win11本地账户登录密码忘了

第一个方法&#xff1a;没有权限&#xff08;可以研究下如何拿到权限&#xff0c;我后来没研究&#xff09; 第二个办法解决问题&#xff1a; 参考这个图&#xff1a; 步骤&#xff1a; 0.背景描述&#xff1a;我wly_yxx的账户&#xff08;类型是管理员&#xff09;知道pin可…

eclipse搭建java web项目

准备条件 eclipsejdk1.8 &#xff08;配置jdk环境&#xff09;apache-tomcat-8.5.97&#xff08;记住安装位置&#xff09; 一 点击完成 开始创建javaweb项目 import java.io.IOException; import java.io.PrintWriter;import javax.servlet.ServletException; import javax.s…

数据库系统概念(第一周)

⚽前言 &#x1f3d0;四个基本概念 一、数据 定义 种类 特点 二、数据库 三、数据库管理系统&#xff08;DBMS&#xff09; 四、 数据库系统&#xff08;DBS&#xff09; &#x1f3c0;数据库系统和文件系统对比 文件系统的弊端 &#x1f94e;数据视图 数据抽象 …

开源模型应用落地-工具使用篇-Spring AI-高阶用法(九)

一、前言 通过“开源模型应用落地-工具使用篇-Spring AI-Function Call&#xff08;八&#xff09;-CSDN博客”文章的学习&#xff0c;已经掌握了如何通过Spring AI集成OpenAI以及如何进行function call的调用&#xff0c;现在将进一步学习Spring AI更高阶的用法&#xff0c;如…

排序——选择排序

基本思想 每一趟在待排序元素中选取关键字最小的元素加入有序子序列。 算法代码 #include <iostream> using namespace std;//选择排序 void SelectSort(int nums[],int n){int i,j,min;for(i0;i<n-1;i){ //一共需要进行 n-1 趟 mini; //记录最小元素的下…

python导出数据到sqlite中

import sqlite3# 数据 data [{username: 张三, age: 33, score: 13},{username: 李四, age: 44, score: 14},{username: 王五, age: 55, score: 15}, ]# 连接SQLite数据库&#xff08;如果不存在则创建&#xff09; conn sqlite3.connect(test.db)# 创建游标对象 cursor con…

云服务器实例重启后,各个微服务的接口(涉及mysql操作的)都用不了了

问题描述&#xff1a; 云服务器被黑客植入挖矿。重启云服务器实例后得到解决&#xff0c;接着把docker&#xff08;zookeeper、redis啥的&#xff09;还有后端jar包啥的都重启了&#xff0c;然后发现后端接口访问不了&#xff0c;只有不涉及数据库操作的接口正常访问&#xff…

一篇论文回顾 Sora 文生视频技术的背景、技术和应用。

一篇论文回顾 Sora 文生视频技术的背景、技术和应用。 追赶 Sora&#xff0c;成为了很多科技公司当下阶段的新目标。研究者们好奇的是&#xff1a;Sora 是如何被 OpenAI 发掘出来的&#xff1f;未来又有哪些演进和应用方向&#xff1f; Sora 的技术报告披露了一些技术细节&…

【论文精读】融合知识图谱和语义匹配的医疗问答系统

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

Java零基础-数组的初始化

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

深度学习与机器学习:互补共进,共绘人工智能宏伟蓝图

在人工智能的广阔天地中&#xff0c;深度学习与机器学习如同两支强大的队伍&#xff0c;各自闪耀着独特的光芒&#xff0c;却又携手共进&#xff0c;共同书写着智能的辉煌篇章。尽管深度学习是机器学习的一个分支&#xff0c;但它们在模型构建、特征提取以及应用场景等多个方面…

FFmpeg--FLV格式

文章目录 FLV组成&#xff1a;字段信息flv headertag headeraduio tag datavideo tag data FLV组成&#xff1a; 一种流媒体格式, 当前主流的视频网站基本都支持FLV格式封装&#xff0c;文件后缀为.flv Previous Tag Size 大小为4个字节&#xff0c;内容为前面一个Tag字节大小…

2024 MCM数学建模美赛2024年A题复盘,思路与经验分享:资源可用性与性别比例 | 七鳃鳗性别比例变化对七鳃鳗种群的影响(三)

目录 分析题目 建立模型 计算结果 分析结果 代码 分析题目 对于第二问&#xff1a;探究七鳃鳗性别比例变化对七鳃鳗种群的影响。我们来分析一下题目。 要探究七鳃鳗性别比例变化对七鳃鳗种群的影响&#xff0c;我们就要搞清楚性别平衡时&#xff0c;它的种群多大&#x…

YUNBEE云贝:3月9日-PostgreSQL中级工程师PGCE认证培训

课程介绍 根据学员建议和市场需求,规划和设计了《PostgreSQL CE 认证课程》,本课程以内部原理、实践实战为主&#xff0c;理论与实践相结合。课程包含PG 简介、安装使用、服务管理、体系结构等基础知识。同时结合一线实战案例&#xff0c; 面向 PG 数据库的日常维护管理、服务和…