SpringBoot中java操作excel【EasyExcel】

news2024/11/25 22:59:03

EasyExcel 处理Excel;简单记录,方便日后查询!

  • 官方文档: Easy Excel (alibaba.com)

一、EasyExcel概述

Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便

二、导入依赖

以 SpringBoot项目为例

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.6.7</version>
</dependency>
<!-- easyexcel -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.3</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
</dependency>

三、读Excel

1、测试数据

test.xlsx 读取该表中数据;文件放入 resources 目录下

在这里插入图片描述

2、创建实体类

所有字段都用 String 类型来接收,接收后可以自行转换

@Data
public class DemoData {
    @ExcelProperty("日期")
    private String date;
    @ExcelProperty("名称")
    private String name;
    @ExcelProperty("数量")
    private String num;
}

3、读取监听器

@Slf4j
public class MyReadListener implements ReadListener<DemoData> {
    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        //log.info("解析到一条数据:{}", JSON.toJSONString(data));
        System.out.println(data.getDate() + " - " + data.getName() + " - " + data.getNum());
    }
    
    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("所有数据解析完成!");
    }
}    

4、读取类

@Slf4j
@Component
public class ExcelUtil {
    /**
     * <p>
     * 1. 创建excel对应的实体对象
     * <p>
     * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器
     * <p>
     * 3. 直接读即可
     */
    @SneakyThrows
    public static void simpleRead() {
        // 有个很重要的点 MyReadListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
        // 写法3:
        File file = ResourceUtils.getFile("classpath:test.xlsx");
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(file, DemoData.class, new MyReadListener())
                .sheet()
            	// 默认头部是1行;该测试数据中是2行
                .headRowNumber(2)
                .doRead();
    }
}    

5、测试运行

public class MyTest {
    public static void main(String[] args) {
        ExcelUtil.simpleRead();
    }
}

在这里插入图片描述

6、日期转换

在监听器中添加 DateTimeFormatter

@Slf4j
public class MyReadListener implements ReadListener<DemoData> {
    static DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd");

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        //log.info("解析到一条数据:{}", JSON.toJSONString(data));
        String date = data.getDate();
        LocalDate localDate = LocalDate.parse(date, dtf);
        System.out.println(localDate + " - " + data.getName() + " - " + data.getNum());
    }
}    
  • 运行测试!转换出错 java.time.format.DateTimeParseException

在这里插入图片描述

原因:测试数据中日期不规范;月份或天数如果是一位时,前面要补0;

期望的格式是:yyyy/MM/dd

建议要读取的excel文件中所有单元格格式都是文本形式;

在这里插入图片描述

修改测试数据,重新测试。更新测试数据后,要 clean 项目后再测试;

在这里插入图片描述

在这里插入图片描述

7、过滤脏数据

在监听器中加入异常处理

	/**
     * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
     *
     * @param exception
     * @param context
     * @throws Exception
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
        // 如果是某一个单元格的转换异常 能获取到具体行号
        // 如果要获取头的信息 配合invokeHeadMap使用
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
            log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
                      excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
        }
    }
  • 重新读取测试

在这里插入图片描述

8、解析成功后统一输出

把解析成功数据先存入集合中,最后统一处理;

修改监听器类

在这里插入图片描述

在这里插入图片描述

9、官方格式转换(推荐)

1> 原理剖析

(个人理解)EasyExcel 会把每个单元格中的数据都当成 String 来读取,再通过各种转换器,转换成需要的类型(实体类中定义的类型),如果转换失败,就抛出异常。在 easyexcel-core-xxx.jar 中有大量的转换器

在这里插入图片描述

格式转换中容易出问题的是日期格式;

以日期转换为例;

在这里插入图片描述

在这里插入图片描述

2> 测试

  • 修改实体类;把日期类型由 String 改为 LocalDate

在这里插入图片描述

  • 监听器中直接获取LocalDate字段值;让系统自动进行格式转换

在这里插入图片描述

  • 运行测试

在这里插入图片描述

全部解析失败;原因很简单:系统默认期望的日期格式为 yyyy-MM-dd,而测试数据中的日期格式为 yyyy/MM/dd,二者不一致

3> @DateTimeFormat(“…”)

在实体类的日期字段上添加 @DateTimeFormat("yyyy/MM/dd") 注解;与测试数据中的日期格式一致。

如果测试数据中的日期格式与官方默认格式一致,可以省略。

在这里插入图片描述

  • 运行测试

在这里插入图片描述

四、写Excel

1、目标数据表

期望生成如下数据表

在这里插入图片描述

  • 复杂表头:多行、字体、颜色、行高、列宽
  • 指定数据格式:日期 yyyy年MM月dd日 ,百分比#.##% 最多二位

2、实体类

WriteDemoData 关键类;

数据表中的样式都在这里面定义

  • fillForegroundColor 为颜色枚举类中的颜色索引值 IndexedColors.RED.getIndex()
  • @DateTimeFormat(“yyyy年MM月dd日”) 为生成后的日期格式
  • @NumberFormat(“#.##%”) 数字格式
@Getter
@Setter
@EqualsAndHashCode
// 头背景设置成红色 IndexedColors.RED.getIndex()
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 10)
// 头字体设置成16
@HeadFontStyle(fontHeightInPoints = 16)
// 内容的背景设置成绿色 IndexedColors.GREEN.getIndex()
//@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 17)
// 内容字体设置成11
//@ContentFontStyle(fontHeightInPoints = 11)
// 内容行高
@ContentRowHeight(20)
// 头部行高
@HeadRowHeight(25)
// 列宽
@ColumnWidth(10)
@AllArgsConstructor
public class WriteDemoData {
    @ExcelProperty({"主标题", "二级主标题", "日期"})
    @DateTimeFormat("yyyy年MM月dd日")
    @ColumnWidth(20)
    private LocalDate date;
    // 字符串的头背景设置成粉红 IndexedColors.PINK.getIndex()
    //@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 14)
    // 字符串的头字体设置成20
    //@HeadFontStyle(fontHeightInPoints = 30)
    // 字符串的内容的背景设置成天蓝 IndexedColors.SKY_BLUE.getIndex()
    //@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 40)
    // 字符串的内容字体设置成20
    //@ContentFontStyle(fontHeightInPoints = 30)
    @ExcelProperty({"主标题", "二级主标题", "名称"})
    private String name;

    @ExcelProperty({"主标题", "二级主标题", "份额"})
    @NumberFormat("#.##%")
    private Double num;
}

3、目标数据

@Slf4j
public class ExcelUtil {
    /**
     * 测试数据
     *
     * @return
     */
    private static List<WriteDemoData> data() {
        List<WriteDemoData> list = new ArrayList<>();
        list.add(new WriteDemoData(LocalDate.parse("2023-07-09"), "张三", 0.1525));
        list.add(new WriteDemoData(LocalDate.parse("2023-07-10"), "李四", 0.2650));
        list.add(new WriteDemoData(LocalDate.parse("2023-07-11"), "王五", 0.1385));
        list.add(new WriteDemoData(LocalDate.parse("2023-07-12"), "小刘", 0.1723));
        list.add(new WriteDemoData(LocalDate.parse("2023-07-13"), "小李", 0.3652));
        list.add(new WriteDemoData(LocalDate.parse("2023-07-14"), "小陈", 0.1234));
        list.add(new WriteDemoData(LocalDate.parse("2023-07-15"), "小赵", 0.863));
        list.add(new WriteDemoData(LocalDate.parse("2023-07-16"), "小可", 0.1329));
        list.add(new WriteDemoData(LocalDate.parse("2023-07-17"), "小钱", -0.1560));
        return list;
    }
}

LocalDate.parse(“2023-07-09”) 中的日期格式与系统默认的格式一致,parse时可以省略DateTimeFormatter

4、写入文件

@Slf4j
public class ExcelUtil {
    @SneakyThrows
    public static void write() {
        // 类路径目录
        Resource resource = new ClassPathResource("");
        String resourceDir = resource.getFile().getAbsolutePath();

        File file = new File(resourceDir, "test_" + System.currentTimeMillis() + ".xlsx");

        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        EasyExcel.write(file, WriteDemoData.class).sheet("模板").doWrite(data());
    }
}

5、运行测试

public class MyTest {
    public static void main(String[] args) {
        ExcelUtil.write();
    }
}

在这里插入图片描述

6、自定义表头

如果表头随内容而变化,需要在生成表的时候才能确定,如下图

在这里插入图片描述

1> 修改实体类

去掉原来实体类中注解定义的表头

在这里插入图片描述

2> 准备表头数据

	/**
     * 动态表头
     * @return
     */
    private static List<List<String>> head() {
        String mainTitle = "自定义主标题";
        String secondTitle = "自定义二级标题";
        
        List<List<String>> list = new ArrayList<>();
        
        List<String> head0 = new ArrayList<>();
        head0.add(mainTitle);
        head0.add(secondTitle);
        head0.add("日期");

        List<String> head1 = new ArrayList<>();
        head1.add(mainTitle);
        head1.add(secondTitle);
        head1.add("名称");

        List<String> head2 = new ArrayList<>();
        head2.add(mainTitle);
        head2.add(secondTitle);
        head2.add("份额");

        list.add(head0);
        list.add(head1);
        list.add(head2);
        return list;
    }

表头整体上是一个list,list中元素又是list,内部的每个list表示表头中的一列,表头有几行,内部list中就有几个元素

在这里插入图片描述

3> 写数据

在这里插入图片描述

4> 运行测试

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端食堂技术周刊第 91 期:2023 npm 状态、TC39 会议回顾、浏览器中的 Sass、React 18 如何提高应用程序性能

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;茶椰生花 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童欧巴。欢迎来到前端食堂技术周刊&#xff0c;我们先来看下…

js基础-练习三

九九乘法表&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthsc, initial-scale1.0"><title>九九乘法表</title><style&g…

5.9 Bootstrap 警告框(Alert)插件

文章目录 Bootstrap 警告框&#xff08;Alert&#xff09;插件用法选项方法事件 Bootstrap 警告框&#xff08;Alert&#xff09;插件 警告框&#xff08;Alert&#xff09;消息大多是用来向终端用户显示诸如警告或确认消息的信息。使用警告框&#xff08;Alert&#xff09;插件…

基于 Flink SQL CDC 数据处理的终极武器

文章目录 一、传统的数据同步方案与 Flink SQL CDC 解决方案1.1 Flink SQL CDC 数据同步与原理解析1.2 基于日志的 CDC 方案介绍1.3 选择 Flink 作为 ETL 工具 二、 基于 Flink SQL CDC 的数据同步方案实践2.1 CDC Streaming ETL2.2 Flink-CDC实践之mysql案例 来源互联网多篇文…

Redis—分布式系统

Redis—分布式系统 &#x1f50e;理解分布式&#x1f50e;分布式—应用服务与数据库服务分离引入更多的应用服务节点理解负载均衡 引入更多的数据库服务节点缓存分库分表 微服务 &#x1f50e;常见概念应用(Application) / 系统(System)模块(Module) / 组件(Component)分布式(D…

nvm 安装 Node 报错:panic: runtime error: index out of range [3] with length 3

最近在搞 TypeScript&#xff0c;然后想着品尝一下 pnpm&#xff0c;但是 pnmp 8.x 最低需要 Node 16.x&#xff0c;但是电脑上暂时还没有该版本&#xff0c;通过 nvm list available 命令查看可用的 Node 版本&#xff1a; nvm list available# 显示如下 | CURRENT | …

【C++进阶】:继承

继承 一.继承的概念和定义1.概念2.定义 二.基类和派生类对象赋值转换三.继承中的作用域四.派生类的默认成员函数五.继承与友元六.继承与静态成员七.复杂的菱形继承及菱形虚拟继承1.二义性2.原理 八.总结 一.继承的概念和定义 1.概念 继承(inheritance)机制是面向对象程序设计使…

虚拟文件描述符VFD

瀚高数据库 目录 环境 文档用途 详细信息 环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;14 文档用途 了解VFD 详细信息 1.相关数据类型 typedef struct vfd{int fd; /* current FD, or VFD_CLOSED if non…

23 自定义控件

案例&#xff1a;组合Spin Box和Horizontal Slider实现联动 新建Qt设计师界面&#xff1a; 选择Widget&#xff1a; 选择类名&#xff08;生成.h、.cpp、.ui文件&#xff09; 在smallWidget.ui中使用Spin Box和Horizontal Slider控件 可以自定义数字区间&#xff1a; 在主窗口w…

第17章 常见函数

创建函数 第一种格式采用关键字function&#xff0c;后跟分配给该代码块的函数名。 function name {commands }第二种 name() { commands }你也必须注意函数名。记住&#xff0c;函数名必须是唯一的&#xff0c;否则也会有问题。如果你重定义了函数&#xff0c;新定义会覆…

【时间复杂度】

旋转数组 题目 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 /* 解题思路&#xff1a;使用三次逆转法&#xff0c;让数组旋转k次 1. 先整体逆转 // 1,2,3,4,5,6,7 // 7 6 5 4 3 2 1 2. 逆转子数组[0, k - 1] // 5 6 7 4 3…

C语言基本结构:顺序、选择和循环

文章目录 前言顺序结构代码讲解 选择结构代码讲解 循环结构总结 前言 在计算机编程中&#xff0c;掌握基本的编程结构是非常重要的。C语言作为一种广泛应用的编程语言&#xff0c;具有丰富的基本结构&#xff0c;包括顺序结构、选择结构和循环结构。这些基本结构为开发人员提供…

RocketMQ主从集群broker无法启动,日志报错

使用vmWare安装的centOS7.9虚拟机&#xff0c;RocketMQ5.1.3 在rocketMQ的bin目录里使用相对路径的方式启动broker&#xff0c;jps查询显示没有启动&#xff0c;日志报错如下 排查配置文件没有问题&#xff0c;nameServer也已经正常启动 更换绝对路径&#xff0c;启动broker&…

flutter:animate_do(flutter中的Animate.css)

简介 做过web开发的应该大部分人都知道Animate.css&#xff0c;它为开发者提供了一系列预定义的动画效果&#xff0c;可以通过简单的CSS类来实现各种动画效果。而animate_do 相当于flutter中的Animate.css,它提供了很多定义好的动画效果 基本使用 官方地址 https://pub-web.…

一文学会redis在springBoot中的使用

“收藏从未停止&#xff0c;练习从未开始”&#xff0c;或许有那么一些好题好方法&#xff0c;在被你选中收藏后却遗忘在收藏夹里积起了灰&#xff1f;今天请务必打开你沉甸甸的收藏重新回顾&#xff0c;分享一下那些曾让你拍案叫绝的好东西吧&#xff01; 一、什么是redis缓存…

【深度学习】【三维重建】windows10环境配置PyTorch3d详细教程

【深度学习】【三维重建】windows10环境配置PyTorch3d详细教程 文章目录 【深度学习】【三维重建】windows10环境配置PyTorch3d详细教程Anaconda31.安装Anaconda32.卸载Anaconda33.修改Anaconda3安装虚拟环境的默认位置 安装PyTorch3d确定版本对应关系源码编译安装Pytorch3d 总…

Day 65: 集成学习之 AdaBoosting (3. 集成器)

代码&#xff1a; package dl;import java.io.FileReader; import weka.core.Instance; import weka.core.Instances;/*** The booster which ensembles base classifiers.*/ public class Booster {/*** Classifiers.*/SimpleClassifier[] classifiers;/*** Number of classi…

解决报错:Can‘t connect to HTTPS URL because the SSL module is not available.

本人今天准备打开安装一个label-studio包&#xff0c;试了很多次&#xff0c;接连报如下错误&#xff0c;因此我就去找了一些解决方案&#xff0c;现在总结如下&#xff1a; 1、报错信息如下 2、解决方案如下&#xff1a; github上有对应的解决方案&#xff0c;链接&#xff…

教师ChatGPT的23种用法

火爆全网的ChatGPT&#xff0c;作为教师应该如何正确使用&#xff1f;本文梳理了教师ChatGPT的23种用法&#xff0c;一起来看看吧&#xff01; 1、回答问题 ChatGPT可用于实时回答问题&#xff0c;使其成为需要快速获取信息的学生的有用工具。 从这个意义上说&#xff0c;Cha…

安卓开发后台应用周期循环获取位置信息上报服务器

问题背景 最近有需求&#xff0c;在APP启动后&#xff0c;退到后台&#xff0c;还要能实现周期获取位置信息上报服务器&#xff0c;研究了一下实现方案。 问题分析 一、APP退到后台后网络请求实现 APP退到后台后&#xff0c;实现周期循环发送网络请求。目前尝试了两种方案是…