EasyExcel实现导入导出

server/2024/9/23 4:16:20/

EasyExcel实现导入导出

目录

    • EasyExcel实现导入导出
    • 1、使用场景
    • 2、特点
    • 3、使用
      • 1、使用EasyExcel进行写操作(下载Excel)
        • 1. 在pom文件中添加对应的依赖
        • 2. 创建实体类,和excel数据对应
        • 3. converter自定义转换器
          • 4、性别枚举类
        • 5.普通导出
        • 6.多sheet表导出
      • 2.使用EasyExcel进行读操作(导入excel)
        • 1、文件读取配置
        • 2、导入

1、使用场景

  1. 数据导出:减轻录入工作量
  2. 数据导入:统计信息归档
  3. 数据传输:异构系统之间数据传输

2、特点

  • Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的fullgc。
  • EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部载到内存中,而是从磁盘上一行行读取数据,逐个解析。
  • EasyExcel采用一 行一 行的解析模式,并将一行的解析结果以观察者的模式通知处理( AnalysisEventListene)。

3、使用

1、使用EasyExcel进行写操作(下载Excel)

1. 在pom文件中添加对应的依赖
java">		<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.4</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.27</version></dependency>
2. 创建实体类,和excel数据对应
java">
@Data
public class TestInfoExcel {//ceshi@ExcelIgnoreprivate Long id;//用户ID自生成@ExcelProperty("用户排序")@ColumnWidth(20)private Integer sort;//名称@ExcelProperty("用户名称")@ColumnWidth(20)private String name;//时间@ExcelProperty("时间")@ColumnWidth(20)/*** 按照指定的格式对日期进行格式化;* */@DateTimeFormat("yyyy-MM-dd HH:mm:ss")private Date insertTime;//身高@ExcelProperty("身高(米)")@NumberFormat("#.##")@ColumnWidth(20)private  Double height;//性别(0男1女)/*** 自定义内容转换器* */@ExcelProperty(value = "性别", converter = GenderConverter.class)@ColumnWidth(10)private  int sex;}
常用注解有:
@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转换成男、女的实例:
性别转换器:

java">
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.example.wenxin.enums.GenderEnum;/*** 性别转换器* */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());}
}
4、性别枚举类
java">
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Getter;import java.util.stream.Stream;/*** @program: wenxin* @ClassName GenderEnum* @description:* @author: XZY* @create: 2024-05-27 13:54* @Version 1.0**/
@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);}}
5.普通导出
java">
/*** 测试(TestInfo)表控制层** @author makejava* @since 2024-05-27 13:34:15*/
@RestController
@RequestMapping("testInfo")
public class TestInfoController {/*** 服务对象*/@Autowiredprivate TestInfoService testInfoService;/*** 设置响应结果** @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();}/*** 导出数据* */@PostMapping("/export/testUser")public void exportUserExcel(HttpServletResponse response,@RequestBody TestInfo info) throws IOException {OutputStream outputStream=response.getOutputStream();try {this.setExcelResponseProp(response, "用户列表");// 模拟根据条件在数据库查询数据List<TestInfoExcel> userList = new ArrayList<>();userList  =  testInfoService.getAllList(info);//这个实现方式非常简单直接,使用EasyExcel的write方法将查询到的数据进行处理,以流的形式写出即可EasyExcel.write(outputStream,TestInfoExcel.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();}}
}
6.多sheet表导出
java">
/*** 测试(TestInfo)表控制层** @author makejava* @since 2024-05-27 13:34:15*/
@RestController
@RequestMapping("testInfo")
public class TestInfoController {/*** 服务对象*/@Autowiredprivate TestInfoService testInfoService;/*** 设置响应结果** @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();}/*** 多sheet导出数据* */@PostMapping("/export/manySheet")public void exportManySheet(HttpServletResponse response,@RequestBody TestInfo info)throws IOException{OutputStream outputStream=response.getOutputStream();ExcelWriter writer = EasyExcel.write(outputStream, TestInfoExcel.class).excelType(ExcelTypeEnum.XLSX).build();try {this.setExcelResponseProp(response, "用户列表");// 模拟根据条件在数据库分页查询数据List<TestInfoExcel> userList = new ArrayList<>();userList  =  testInfoService.getAllList(info);//创建新的sheet页WriteSheet writeSheet = EasyExcel.writerSheet("用户信息" + "一").build();//将list集合中的对象写到对应的sheet中去writer.write(userList,writeSheet);info.setName("啊");List<TestInfoExcel> allList = testInfoService.getAllList(info);WriteSheet writeSheet2 = EasyExcel.writerSheet("用户信息" + "二").build();writer.write(allList,writeSheet2);} catch (IOException e) {throw new RuntimeException(e);//给提示todo}finally {writer.finish();outputStream.flush();outputStream.close();}}}

2.使用EasyExcel进行读操作(导入excel)

1、文件读取配置
java">
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.example.wenxin.entity.TestInfo;import java.util.ArrayList;
import java.util.List;/*** 自定义监听器,对下载的excel中的数据进行校验* */
public class UserListener extends AnalysisEventListener {List<String> names = new ArrayList<>();/*** 每解析一行,回调该方法** @param data* @param context*/@Overridepublic void invoke(Object data, AnalysisContext context) {//校验名称String name = ((TestInfo) data).getName();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();}
}
2、导入
java">
/*** 测试(TestInfo)表控制层** @author makejava* @since 2024-05-27 13:34:15*/
@RestController
@RequestMapping("testInfo")
public class TestInfoController {/*** 服务对象*/@Autowiredprivate TestInfoService testInfoService;/*** 导入数据* */@PostMapping(value = "/importData")@Transactionalpublic void importData(MultipartFile file){try {//获取文件的输入流InputStream inputStream = file.getInputStream();List<TestInfo> lst = EasyExcel.read(inputStream) //调用read方法//注册自定义监听器,字段校验可以在监听器内实现.registerReadListener(new UserListener()).head(TestInfo.class) //对应导入的实体类.sheet(0) //导入数据的sheet页编号,0代表第一个sheet页,如果不填,则会导入所有sheet页的数据.headRowNumber(1) //列表头行数,1代表列表头有1行,第二行开始为数据行.doReadSync(); //开始读Excel,返回一个List<T>集合,继续后续入库操作//模拟导入数据库操作for (TestInfo userDO:lst){System.out.println(userDO.toString());}boolean b = testInfoService.saveBatch(lst);}catch (IOException exception){throw new  RuntimeException(exception);}}}

http://www.ppmy.cn/server/46276.html

相关文章

【Tlias智能学习辅助系统】03 部门管理 前后端联调

Tlias智能学习辅助系统 03 部门管理 前后端联调 前端环境 前端环境 链接&#xff1a;https://pan.quark.cn/s/8720156ed6bf 提取码&#xff1a;aGeR 解压后放在一个不包含中文的文件夹下&#xff0c;双击 nginx.exe 启动服务 跨域的问题已经被nginx代理转发了&#xff0c;所以…

电商物流查询解决方案助力提升消费者体验

截至2023年12月&#xff0c;中国网络购物用户规模达9.15亿人&#xff0c;占网民整体的83.8%。这一庞大的数字不仅展现了电子商务的蓬勃发展&#xff0c;也标志着数字零售企业营销战略的转变——从以产品和流量为核心&#xff0c;到用户为王的新阶段。因此&#xff0c;提升消费者…

基础—SQL—DQL(数据查询语言)聚合函数

一、引言 一般情况下&#xff0c;我们在进行分组查询的时候&#xff0c;一般配合着聚合函数来进行操作&#xff0c;所以先了解和学习聚合函数再学习和操作分组查询。 二、DQL—聚合函数 1、介绍 聚合函数指的是讲一列数据作为一个整体&#xff0c;进行纵向的计算。 2、常见…

panic 、asset、crash 的含义和区别

在编程中&#xff0c;“panic” 和 “assert” 都是用于处理错误和异常情况的机制&#xff0c;但在不同的编程语言和框架中有一些区别。 panic&#xff1a; 含义&#xff1a;通常表示程序发生了无法恢复的错误或异常情况&#xff0c;需要立即终止程序的执行。 用法&#xff1…

LeeCode热题100(两数之和)

本文纯干货&#xff0c;看不懂来打我&#xff01; 自己先去看一下第一题的题目两数之和&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 简单来说就是让你在一个数组里面找两个数&#xff0c;这两个数的和必须满足等于目标值target才行。 我认为你要是没有思路的话&a…

使用dockerfile快速构建一个带ssh的docker镜像

不多说先给代码 FROM ubuntu:22.04 # 基础镜像 可替换为其他镜像 USER root RUN echo root:root |chpasswd RUN apt-get update -y \&& apt-get install -y git wget curl RUN apt-get install -y openssh-server vim && apt clean \&& rm -rf /tmp/…

【模型架构】学习RNN、LSTM、TextCNN和Transformer以及PyTorch代码实现

一、前言 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;模型架构的不断发展极大地推动了技术的进步。从早期的循环神经网络&#xff08;RNN&#xff09;到长短期记忆网络&#xff08;LSTM&#xff09;、Transformer再到当下火热的Mamba&#xff08;放在下一节&a…

新手快速上手IDEA【常用快捷键】

目录 一、常用二、进阶&#xff08;提高编码速度&#xff09;三、其他四、查找、替换与关闭最后 一、常用 说明快捷键复制代码ctrl c粘贴ctrl v剪切ctrl x撤销ctrl z反撤销ctrl shift z保存-save allctrl s全选-select allctrl a 二、进阶&#xff08;提高编码速度&a…