一、EasyExcel介绍
EasyExcel
是一个高效、低内存占用的Excel
处理框架,提供了简洁易用的API
接口,使得我们能更加高效、灵活的处理Excel
文件。当然,EasyExcel
并非一个完全从零构建的开源项目,而是阿里巴巴在POI
的基础上做了进一步封装,大家能从 EasyExcel官网上看到一段这样的描述:
POI
也好,JXL
也罢,它们都存在一个严重的问题就是非常的耗内存,尽管POI
提供了一套SAX
模型能在一定程度上解决OOM
问题,但它仍然存在一些缺陷,对内存消耗依旧较大。因此,EasyExcel
重写了POI
对07
版Excel
文件的解析,一个3MB
的文件用原生的POI-SAX
去解析,需要消耗100MB
左右的内存,改用EasyExcel
可以降低到几MB,并且再大的文件也不会出现内存溢出;03
版依赖POI-SAX
模式,也在上层做了模型转换的封装,让使用者更加简单方便。
同时,官网文档上还贴了一张性能测试图:
一个75MB
的Excel
文件,每行数据二十五列,总计46W
条数据,能够在23
秒解析、读取完成,并且仅消耗了16MB
内存,这个结果无疑是惊人的!同时,如果内存足够充裕,还可以开启急速模式换取更快的处理性能。
当然,这个数据来源于实验环境,在硬件配置较差、或读取数据后还需要清洗、落库等情况下,性能上会有所差异。但是不管怎么说,在资源占用、性能方面都完胜POI
原生的处理方式。好了,EasyExcel
究竟怎么用,如何实现百万级数据的读写?这将是接下来的主题。
PS:当报表数据达到大几十万、百万级别时,不管是读取还是写入操作,执行都会相对缓慢,因为还要考虑各种业务耗时,为了尽可能的提升性能,通常还需要结合多线程技术去优化,所以后面会单开篇章进行解读,本章先来聊
EasyExcel
本身。毕竟饭得一口一口吃,想要基于EasyExcel
实现大文件处理,前提是你得掌握它的基本用法。
OK,对EasyExcel
有了基本认知后,下面来一起快速上手实操一下,首先咱们需要导入下相关依赖:
这里我们可以使用最新的4.0.2
版本,也可以选择之前的稳定版本,3.1.x
以后的版本API
大致相同,新的版本也会向前兼容(3.1.x
之前的版本,部分API
可能在高版本被废弃),关于POI、JDK
版本适配问题,具体可参考 官网-版本说明。
1.1、EasyExcel写入Excel案例
导入依赖后,我们先来熟悉下用法,现在有一个实体类如下:
假设有一个ZhuZi
集合,目前想把其中的数据,生成并写入到Excel
文件该怎么做?十分简单:
案例中先创建了一个集合对象,而后指定了文件名称,最后直接通过EasyExcel
写出了这个集合,来看运行后的效果:
效果很明显,在当前工程的根目录下出现了一个竹子数据-1723622954549.xlsx
文件,打开后能正常看到集合里的数据,并且Date
格式的日期也做了自动转换!
1.2、EasyExcel读取Excel案例
简单看完写入操作后,下面来看看如何读取Excel
文件里的数据,咱们就以前面生成的文件作为解析目标,首先需要定义一个监听器:
这个监听器很简单,无非就是将每条读取到的数据添加到data
集合里,然后对外提供了一个获取数据的方法(其实这个监听器不定义也可以,因为EasyExcel
有默认的监听器),下面来看读取代码:
写入数据用的是EasyExcel.write()
方法,而读取数据则是read()
方法,主要传递了三个参数,Excel
文件地址、数据转换的目标类型、负责监听数据解析的zhuZiListener
对象。接着,会使用zhuZis
集合来接收读到的数据,最后来看看执行结果:
从输出结果来看,这里成功将之前写入的一条数据解析了出来,这说明咱们成功实现了excel
读取功能。大家会发现EasyExcel
就如同它的名字一样,使用起来So Easy
!
二、EasyExcel数据模型注解
通过两个小案例体验了一下EasyExcel
的效果,细心的小伙伴应该发现了,不管是读取还是写入文件,都需要传递一个具体的class
,这个类的专业叫法称为数据模型类,主要是用来映射Java对象与Excel
列的匹配关系,为了更好的满足各种报表需求,EasyExcel
提供了一系列数据模型类的注解来辅助,这些注解大体可分为两类,下面一起看看。
PS:本阶段会先对注解进行说明,后面会结合具体的案例来演示其效果。不过为了更好的阐述为什么需要这些注解,先来看看“没有注解”时,
EasyExcel
会如何匹配Java字段与Excel
列的映射关系。
Excel
文件打开后是经典的表格式数据,而表格的列会从左到右依次分布。基于这种背景下,EasyExcel
会根据列的先后顺序,与Java类字段的定义顺序进行匹配,每当解析到一条数据,就会根据定义的顺序为字段赋值(写入数据则相反的过程)。当然,大家可以试着去验证一下,比如Excel
第一列写字符串,但Java类的第一个字段定义成int
,这时就会抛出类型转换异常。
2.1、读写通用注解
顾名思义,通用注解是指在类、字段上添加后,对于读写场景都会生效的注解,而这些通用注解也是最常用的,且必须掌握的!
@ExcelProperty注解
- 作用范围:数据实体类的字段上;
- 注解释义:用于映射
Excel
列与实体类字段的匹配关系; - 可选参数:
value
:以列名形式匹配excel列,多行头会匹配最后一行;order
:以权重形式匹配excel列,值越小越靠前,优先级高于value
;index
:以索引形式匹配excel列,优先级高于value、order
;converter
:数据格式转换器,解析时会根据指定的转换器处理对应数据。
该注解是EasyExcel
中最重要的注解,它提供了列名、权重、索引三种列匹配模式,不过在解析excel
文件时,官方只建议同时使用一种,因为就算指定了多种,它们也会有优先级关系,大可不必多此一举。
但是,@ExcelProperty
在生成excel
文件时也有用!大家回想前面的第一个案例,当我们将ZhuZi
数据写入文件时,默认的列名就是类的字段名称,而当你给类字段添加该注解后,其中value
指定的名称,也会成为excel
列的名称。在写入场景中,value、index
可以一起使用,前者用于控制列名,后者用于控制列的顺序,示例:
这代表name
字段在excel
文件的列名为“姓名”,位于表格的第二列(B
列)。小结一下,ExcelProperty
注解在读取用于映射excel表头,在写入时用于控制生成的excel表头。
@ExcelIgnore注解
- 作用范围:数据实体类的字段上;
- 注解释义:当前字段不参与excel列的匹配,即处理时忽略该字段;
在默认情况下,数据模型类中的所有字段都会参与匹配,可如果你定义的Java类中,有些字段在读写时并不需要参与进来,这时就可以给对应字段加上@ExcelIgnore
注解,具备该注解的字段会被忽略。
@ExcelIgnoreUnannotated注解
- 作用范围:数据模型类上;
- 注解释义:匹配列时忽略所有未使用
@ExcelProperty
注解的字段;
如果类中许多字段都不想参与excel
读写,而你又嫌挨个加@ExcelIgnore
注解麻烦,这时就可以直接在类上加一个@ExcelIgnoreUnannotated
注解,以此来忽略所有未添加@ExcelProperty
注解的字段。
@DateTimeFormat注解
- 作用范围:数据实体类的字段上;
- 注解释义:用
String
接收日期数据时,会根据指定格式转换日期; - 可选参数:
value
:日期数据转换为字符串的目标格式;use1904windowing
:excel日期数据默认从1900
年开始,但有些会从1904
开始;
在解析excel
文件时,如果使用String
字段接收日期数据,就会根据指定的格式转换数据,格式可以参考java.text.SimpleDateFormat
的写法,例如yyyy-MM-dd HH:mm:ss
。而在往excel
写数据时,如果Java中的字段类型为Date、LocalDate、LocalDateTime
等日期类型,则会将日期数据转换为指定的格式写入对应列。
@NumberFormat注解
- 作用范围:数据实体类的字段上;
- 注解释义:用
String
接收数值数据时,会根据指定格式转换数值; - 可选参数:
value
:数值转换为字符串的目标格式;roundingMode
:数值格式化时的舍入模式,如四舍五入、向上取整等;
这个注解和前一个注解类似,只不过是用于将非整数类型的数值数据转换成给定格式,格式可以参考java.text.DecimalFormat
的写法,如#.##
。除了可以指定格式外,还可以指定舍入模式,枚举可参考java.math.RoundingMode
类。
2.2、Excel生成注解
好了,上阶段看了读写通用的注解,下面来看看生成excel
数据时会用到的注解。
@ColumnWidth注解
- 作用范围:数据模型类上、字段上;
- 注解释义:设置列的宽度;
这个注解如果加在类上,则会对所有字段生效;如果单独加在某个字段上,则只对特定的列有效,单位是px
。
@ContentFontStyle注解
- 作用范围:数据模型类上、字段上;
- 注解释义:用于设置单元格内容字体格式的注解;
- 可选参数:
fontName
:字体名称,如“黑体、宋体、Arial”等;fontHeightInPoints
:字体高度,以磅为单位;italic
:是否设置斜体(字体倾斜);strikeout
:是否设置删除线;color
:字体的颜色,通过RGB
值来设置;typeOffset
:偏移量,用于调整字体的位置;underline
:是否添加下划线;bold
:是否对字体加粗;charset
:设置编码格式,只能对全局生效(字段上设置无效)。
这个注解用于设置主体内容的字体样式(不包含表头),与上个注解同理,加在类上对整个excel
文件生效,加在字段上只对单列有效,可以通过该注解来设置字体风格、高度、是否斜体等属性。
@ContentRowHeight注解
- 作用范围:数据模型类上;
- 注解释义:用于设置行高。
这个注解只能加在类上面,作用就是设置单元格的高度,但这里不能像Excel
那样精准设置不同行的高度,只能设置所有单元格统一的高度。
@ContentStyle注解
- 作用范围:数据模型类上、字段上;
- 注解释义:用于设置内容格式;
- 可选参数:
dataFormat
:数据格式,对应excel
的内置数据格式;hidden
:是否隐藏对应的列;locked
:是否锁定对应的列;quotePrefix
:是否开启`引号前缀,数字或工时以字符串展示;wrapped
:设置文本是否应该换行(自动根据内容长度换行);horizontalAlignment
:水平对齐方式,如居中、左对齐等;verticalAlignment
:垂直对齐方式,如上对齐、下对齐等;rotation
:设置文本旋转角度,xls
是-90~90
,xlsx
是0~180
度;indent
:设置单元格中缩进文本的空格数;borderLeft
:设置左边框样式,还有右、上、下三个类似参数;leftBorderColor
:左边框颜色,还有右、上、下三个类似参数;fillPatternType
:设置填充类型;fillBackgroundColor
:设置填充的背景色;fillForegroundColor
:设置填充的前景色;shrinkToFit
:是否开启单元格自动大小(根据内容长度来自动调整);
这个注解作用很多,主要是设置excel内容的整体样式,包括数据格式、是否隐藏某列、是否使用边框、对齐方式、边框颜色等属性,但通常用的不多,因为报表处理更多的关注数据读写本身,而不是样式方面。
@HeadFontStyle注解
- 作用范围:数据模型类上、字段上;
- 注解释义:用于定制标题字体格式。
这个注解的作用和可选参数,与@ContentFontStyle
注解类似,不过这个注解是针对列头(表头)有效罢了。
@HeadRowHeight注解
- 作用范围:数据模型类上;
- 注解释义:用于设置标题行的行高。
此注解的作用参考@ContentStyle
注解,当前注解只对表头生效。
@HeadStyle注解
- 作用范围:数据模型类上
- 注解释义:用于设置标题样式。
该注解的作用和可选参数参考@ContentRowHeight
注解,但是当前注解只对表头生效。
@OnceAbsoluteMerge注解
- 作用范围:数据模型类上;
- 注解释义:用于合并指定的单元格;
- 可选参数:
firstRowIndex
:从哪行开始合并;lastRowIndex
:到哪行结束合并;firstColumnIndex
:从哪列开始合并;lastColumnIndex
:到哪列结束合并。
从这个注解提供的可选参数就能看出具体作用,这是通过单元格行、列索引的方式,指定生成excel
文件时要合并的区域。不过要注意,使用该注解只能合并一次(对应OnceAbsoluteMerge
这个合并策略类)。
@ContentLoopMerge注解
- 作用范围:数据模型类的字段上;
- 注解释义:用于合并单元格;
- 可选参数:
eachRow
:指定每x
行合并为一行;columnExtend
:指定每x
列合并为一列。
该注解也是用于合并单元格的,但是可以合并多次,不过只能实现每隔n
个单元格合并,使用起来限制很大,通常也不会选择通过这种注解的形式来合并单元格,这里了解即可。
三、EasyExcel核心API全解
好了,前面EasyExcel
提供的注解大致都讲了一遍,但想要更好的掌握EasyExcel
,还得对其核心API
有所了解才行,为此,下面一起来看下它的核心API。
3.1、EasyExcel入口类
基于一开始的简单读写案例,会发现所有excel
相关的动作都始于EasyExcel
这个类,而这个类也被称为入口类,主要用来构建各种excel
操作的实例(如读、写)。不过当你点进这个类的源码:
会发现是个空实现,EasyExcel
类直接继承了EasyExcelFactory
,拥有的所有API
底层也由EasyExcelFactory
提供,所以,我们直接分析EasyExcelFactory
即可:
对于重复的方法不在说明,上面列出的是EasyExcelFactory
类中的核心方法,其作用主要是构建读、写excel
文件的对象,以便于后续读取、写入excel
数据。简单了解这个入口类后,下面来看看这些构建出的读写对象。
3.2、Excel读对象
EasyExcel
中,与读取数据相关的核心类主要有三个:ExcelReaderBuilder、ExcelReaderSheetBuilder、ReadListener
,这三各类的具体作用是啥呢?下面挨个剖析。
3.2.1、ExcelReaderBuilder
ExcelReaderBuilder
只是为了满足链式调用封装出的Builder
类,其内部会构建一个读取工作簿的ReadWorkbook
对象,这就相当于你在实体类上加了个@Builder
注解。
好了,所谓的工作簿,可以简单理解成一个Excel
文件,下面看看它的API
(实际上就是在设置读取excel
文件时的属性):
converter
:数据转换器,默认内置了很多,如果读取的数据需要特定转换,则可以按需添加;registerReadListener
:注册监听器,读取数据时会触发已注册的监听器(可以注册多个);headRowNumber
:excel
文件中头的行数,默认为1,文件的列头是多行时设置;head
:excel数据的表头列表(以字符串列表形式指定),官方建议使用模型类的方式;clazz
:与excel数据映射的模型类,与head
二选一,都未指定时会用Map
读取所有数据;autoTrim
:是否开启自动去除前后空格,开启后会对读到的头信息、数据去空格;use1904windowing
:日期格式是否以1904
年开始,兼容特殊的excel
文件;useScientificFormat
:数字转文本的时候在较大的数值的是否是否采用科学计数法;
上述这些参数,也是ExcelReaderSheetBuilder
对象具备的通用属性,下面再来看些关于ExcelReaderBuilder
的独特参数:
excelType
:指定要读取的excel
文件类型,支持XLS、XLSX、CSV
;inputStream
:指定读取数据的文件流,底层会创建临时文件来转换数据;file
:要读取数据的目标文件,与inputStream
二选一;mandatoryUseInputStream
:强制从流中读数据(不会创建临时文件,性能会变差);charset
:设置文件内容编码格式,读取CSV
文件时有效,默认UTF-8
;autoCloseStream
:读取完毕后是否自动关闭读取的流(默认开启);readCache
:设置读取数据的缓存配置(默认5M
内使用内存,其余使用EhCache
);readCacheSelector
:设置什么时候使用内存、什么时候使用磁盘来存储缓存数据;ignoreEmptyRow
:读取数据行时,是否忽略所有字段为空的行,默认开启;password
:读取excel
文件的密码(文件加密时使用);xlsxSAXParserFactoryName
:指定POI-SAX
模式读取使用的实现类;useDefaultListener
:是否使用默认监听器来将excel
数据行转换为Java对象;extraReadSet
:接收额外需要读取内容的set
,如评论、超链接、合并单元的内容;readDefaultReturn
:对于excel中不点击单元格看不到的内容数据、接收时的格式;
了解这些参数后,我们在读取Excel
文件时如何设置呢?先来看看最开始读取文件的代码:
这行代码中,EasyExcel.read()
方法执行后,会返回一个ExcelReaderBuilder
对象,执行sheet()
方法后会返回一个ExcelReaderSheetBuilder
对象,而最后的doRead()
方法则是触发真正解析excel
、读取数据的步骤。
既然如此,那么我们在调用EasyExcel.read()
方法后、sheet()
方法前,就可以继续调用上面提到的一系列方法设置参数,例如:
这里通过excelType()
将读取目标的格式切换成了.csv
文件,而其余参数的设置方式大同小异,这里不再一一演示。
3.2.2、ExcelReaderSheetBuilder
与ExcelReaderBuilder
对象一样,ExcelReaderSheetBuilder
内部维护着一个ReadSheet
对象,Builder
类单纯用于满足链式调用的编程风格。
接触过Excel
的小伙伴应该都知道一点,一个Excel
文件内部可以维护多个Sheet
(工作表单),而ReadSheet
就是专门用来读取表单数据的类,前面在聊ExcelReaderBuilder
类时提到的公用参数,ReadSheet
也同样具备,那么相同的参数作用在不同的对象上,区别是什么?
很简单,如果针对整个工作簿设置的参数,会对所有Sheet
生效;单独针对ReadSheet
设置的参数,则只针对特定的表单有效。如果一个文件的不同Sheet
数据格式不一样,就可以基于这种特性来实现不同的解析逻辑。那么下面来看看ReadSheet
独有的参数:
sheetNo
:指定需要读取数据的目标Sheet
编码(默认为0,表示读第一个);sheetName
:根据表单名字去匹配要读取数据的目标Sheet
。
是的,你没有看错,ReadSheet
的独立参数就这两个,如果你在创建表单读取对象时就已经指定了编号、名称,那么这两个参数甚至也用不到。好了,那如何针对不同Sheet
设置参数呢?
大家还记得ReadSheet
对象是在何时创建的吗?当调用.sheet()
方法后创建的,所以为Sheet
设置参数很简单:
上面这行代码表示从第二个Sheet
中读取数据,而这个表单里的表头是两行,即:从第三行才正式开始读取数据。
3.2.3、ReadListener
了解上面两个读取数据的核心类后,接着来看看另一个读取数据的关键类:读取监听器,EasyExcel
框架中的监听器体系结构如下:
Listener
:内部为空实现,仅抽象为顶层接口,用于维护体系结构及拓展性;ReadListener
:读取监听器的顶层接口,读取excel
文件时会被触发;IgnoreExceptionReadListener
:读取时发生异常会触发的监听器,可以在这里处理异常确保读取不会终止;AnalysisEventListener
:解析事件监听器,每当读取到一行数据,都会触发该类或子类的invoke()
方法;PageReadListener
:分页读取监听器,当读取的数据量达到指定的数量会被触发,处理完一页后才会读下页数据;ModelBuildEventListener
:默认装载的监听器,用于在解析到excel
数据行时,将数据转换为指定类对象;SyncReadListener
:解析事件监听器的子类,默认会将所有数据行添加到一个Object
集合(使用小数据场景);ZhuZiListener
:自定义的监听器,继承自解析事件监听器,内部带有自定义的业务逻辑。
在调用EasyExcel.read()
方法时,传递的第三个入参就是监听器,或者可以通过registerReadListener()
来注册,不过有一点要注意:同一个工作簿或表单读取对象,可以同时注册多个监听器。这就有点类似于Filter
过滤器链一样,而EasyExcel
存在多个监听器时,会根据添加的顺序来触发。
上面列出的一堆监听器,重点关注AnalysisEventListener
即可,因为在实现导入的业务时,通常会继承它来自定义处理业务数据的逻辑,比如可以在invoke()
方法里实现数据校验、落库等逻辑,而前面咱们定义的ZhuZiListener
也继承于它~
3.3、Excel写对象
关于Excel
数据写入的核心类,主要ExcelWriterBuilder、ExcelWriterSheetBuilder、ExcelWriterTableBuilder、WriterHandler
这四个,将这四个搞明白后,玩转EasyExcel
写入就很简单啦~
3.3.1、ExcelWriterBuilder
ExcelWriterBuilder
代表一个写入工作簿对象,也就是调用EasyExcel.writer()
方法返回的对象,内部维护着一个WriteWorkbook
对象,它支持设置的参数列表如下:
converter
:数据转换器,如果读取的数据需要特定转换,则可以按需添加;head
:excel数据的表头列表(以字符串列表形式指定);clazz
:与excel数据映射的模型类,与head
二选一;autoTrim
:是否开启自动去除前后空格,开启后写入数据前会对数据去空格;use1904windowing
:日期格式是否以1904
年开始,兼容特殊的excel
文件;useScientificFormat
:数字转文本的时候在较大的数值的是否是否采用科学计数法;
上面这些是ExcelReaderBuilder
类中也存在通用参数,甚至作用都是一模一样的,因此不做过多说明,继续往下看:
registerWriteHandler
:注册写入处理器,总共有工作簿、表单、数据行、单元格四个级别;relativeHeadRowIndex
:写入到excel
时,和顶部间隔几行,默认为0
;needHead
:是否需要生成数据表头(列头),默认开启;useDefaultStyle
:是否使用默认的样式(即简单写入案例中那个样式);automaticMergeHead
:是否开启自动合并头,头中相同的字段名,上下左右都会尝试合并;excludeColumnIndexes
:写入数据时,数据模型类中要忽略的字段下标;excludeColumnFieldNames
:写入数据时,数据模型类中要忽略的字段名称;includeColumnIndexes
:写入数据时,只需要导出的字段下标;includeColumnFieldNames
:写入数据时,只需要导出的字段名称;orderByIncludeColumn
:是否开启字段排序,默认不开启(根据类字段或指定head排序);filedCacheLocation
:数据模型的字段缓存模式,默认为ThreadLocal
,可以改为纯内存或不缓存。
上述参数是当前对象、WriterSheet、WriterTable
三者皆备的通用参数,再来看看ExcelWriterBuilder
的专属参数:
excelType
:写出的excel
文件类型;charset
:写出的数据编码格式(仅CSV
支持);password
:为生成的文件设置查看密码;autoCloseStream
:写入完成后是否自动关闭流,默认开启;file
:数据要写入的目标文件;outputStream
:数据要写入的目标流;templateFile
:模板文件(填充场景使用);templateInputStream
:模板文件流(填充场景使用);inMemory
:是否基于内存生成文件,默认不开启,会生成临时文件;writeExcelOnException
:写入过程抛出异常时,是否尝试把已有数据写入excel,默认关闭。
这些参数在写入excel
时怎么设置呢?同样先来看最开始的写入案例:
调用EasyExcel.write()
方法后会得到一个ExcelWriterBuilder
对象,而执行sheet()
后会返回一个ExcelWriterSheetBuilder
对象,最后的doWrite()
方法代表真正触发数据写入逻辑。为此,想要设置这些参数,只要在.write()
方法之后、.sheet()
方法之前调用对应方法即可。
3.3.2、ExcelWriterSheetBuilder
ExcelWriterSheetBuilder
内部维护着一个WriteSheet
对象,代表表单写入对象,和表单读取对象类似,与上一个对象的唯一区别就在于:可以针对某一个具体sheet
去设置参数,而WriteSheet
具备的独立参数也就俩:
sheetNo
:将数据写入指定编码的Sheet
表单(默认为0);sheetName
:将数据写入到指定名字的表单中。
如果要为单独某一个表单设置参数,则只需要在调用了sheet()
方法之后、调用doWrite()
方法之前设置即可。
3.3.3、ExcelWriterTableBuilder
与读取Excel
时不同,写入时多了一个WriterTable
类,这个类的作用是:按表格形式写入数据到Excel文件中,即:可以向一个表单里面写入多个数据表格,示意图如下:
而表格的专属参数只有一个,即:
tableNo
:需要写入的表格编码,默认值为0;
如果只往sheet
写入一个表格,这时就算把tableNo
设置成999
都没用。只有当一个sheet
里写入多个表格时,就可以通过这个编号来控制先后顺序,而表格之间可以通过relativeHeadRowIndex
这个通用参数来控制间隔。
3.3.4、WriteHandler
写入处理器WriteHandler
,是EasyExcel
框架在生成excel
时非常重要的组件,它可以在写入过程中执行各种自定义的操作,比如自定义单元格样式、自定义合并单元格、数据行等。
因为框架内部实现了非常多的子类,所以整个WriteHandler
体系比读取监听器大上很多,光WriteHandler
就有二十多个子类,但其中很多我们用不到,因此就不一一做分析了,这里介绍几个较为重要的子类:
CellWriteHandler
:单元格写入处理器,每写入一个单元格时会被触发;RowWriteHandler
:数据行写入处理器,每写入一行数据时会被触发;SheetWriteHandler
:表单写入处理器,每写入一个表单时会被触发;WorkbookWriteHandler
:工作簿写入处理器,写入一个excel
文件时被触发;......
其实上面列出的四个类依旧是接口,大家可以根据具体的需求选择性实现,你可以在实现类里精准控制每个单元格的样式、更改内容、合并等,最后将其注册到对应的工作簿、表单写入对象上就能生效。
3.4、Excel导入导出接口
OK,前面学习了EasyExcel
框架的核心类与API
后,下面来实现下excel
导入、导出接口。
想要实现导入导出接口,关键点就在于如何从网络请求中读取Excel
数据,以及如何将生成的excel
文件返回,其实这个很简单,大家回想下前面读写核心类的参数,是不是支持通过流来指定要读取/写入的目标?因此,我们可以基于输入/输出流来实现,如下:
观察上述代码,基于EasyExcel
来实现导入、导出格外简单,导入时,只需要通过MultipartFile
对象来接收excel
文件,然后从它的流中读取数据解析即可。当然,如果你想对excel
表里的数据进行业务处理,可以选择在数据导入完成后,也可以直接在自定义的Listener
监听器类中,读到一条数据就处理一次。
再来看导出接口,这里会基于response
对象来实现,首先会将响应的数据声明为Excel
文件,等待EasyExcel
生成数据后,就会通过的OutputStream
输出流返回给调用方。只不过要注意,如果文件名是中文,则需要在内部先做UTF-8
编码,否则调用方得到的文件名称就会乱码。
PS:实际上这个案例非常简单,后面的章节会详细讲述多个案例,以此来对应日常开发过程中的各种导入导出需求。
四、总结
就目前为止,前面已经对EasyExcel
框架本身做了较为全面的阐述,掌握本文提到的这些API
后,相信能让你在以后使用EasyExcel
的过程中如鱼得水。
不过,由于本文更多的是在聊框架本身(核心类、API
等),所以并未提供与实际业务需求贴合的实战案例。起初的简单读写演示也好,最后的导入导出接口也罢,都只是十分简单的例子,这对存在报表需求的小伙伴而言,并不会提供太大的参考价值。
同时,尽管EasyExcel
相较于原生的POI
使用方式来说,已经简单了不知道多少倍,可每次读取不同的数据,都需要定义不同的Listener
监听器;每次导出不同的Excel
也需要重复设置相关信息……
那这些能否抽象出公共代码或工具类呢?答案是当然可以,后面一节内容就会讲述:该如何封装通用的工具类与监听器,以及提供多个接近实际业务的场景案例。
作者:竹子爱熊猫
链接:https://juejin.cn/post/7405158045662576640
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。