SpringBoot学习(⼆⼗五):SpringBoot整合EasyExcel,操作Exc。。。
前⾔
Spring Boot系列:
EasyExcel
EasyExcel是阿⾥巴巴开源的⼀个excel处理框架,以使⽤简单、节省内存著称,能避免OOM(使⽤poi可能会OOM)。
EasyExcel能⼤⼤减少占⽤内存的主要原因是,在解析Excel时没有将⽂件⼀次性全部加载到内存中,⽽是从磁盘上⼀⾏⾏读取数据,逐个解析。
以下是官⽅的介绍:
Java解析、⽣成Excel⽐较有名的框架有Apache poi、jxl。但他们都存在⼀个严重的问题就是⾮常的耗内存,poi有⼀套SAX模式的API可以⼀定程度的解决⼀些内存溢出的问题,但POI还是有⼀些缺陷,⽐如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很⼤。easyexcel重写了poi对07版Excel的解析,能够原本⼀个3M的excel⽤POI sax依然需要100M左右内存降低到⼏M,并且再⼤的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使⽤者更加简单⽅便。
性能:64M内存1分钟内读取75M(46W⾏25列)的Excel,当然还有急速模式能更快,但是内存占⽤会在100M多⼀点
Spring Boot集成EasyExcel
1、添加依赖,EasyExcel的更新是特快的,对于EasyExcel,我的建议是使⽤较新的版本,因为⽐较旧的版本会有些⼩bug,所以我⽤的是最新的2.2.6
<!-- mvnrepository/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.6</version>
</dependency>
下⾯我们来按照官⽹的例⼦来实践下easyexcel的使⽤,以下只演⽰最基础的读写,需要更复杂的功能请查看官⽅⽂档
写excel
1、创建excel对应的实体对象
@Data
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("⽇期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}
解析: @ExcelProperty表⽰excel的列名,写⼊excle时会⾃动帮我们创建列名,并将相应的数据赋值到对应的列中
2、获取写⼊数据,在这⾥是⾃⼰⽣成的数据对象,实际中,我们根据⾃⼰业务来⽣成数据对象
//创建写⼊数据
private List<DemoData>data(){
List<DemoData> list =new ArrayList<DemoData>();
for(int i =0; i <10; i++){
DemoData data =new DemoData();
data.setString("字符串"+ i);
data.setDate(new Date());
data.setDoubleData(0.56);
getsavefilenamelist.add(data);
}
return list;
}
3、测试写⼊excel
@Test
public void simpleWrite(){
// 写法1
String fileName ="simpleWrite"+ System.currentTimeMillis()+".xlsx";
System.out.Path());
// 这⾥需要指定写⽤哪个class去写,然后写到第⼀个sheet,名字为模板然后⽂件流会⾃动关闭
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
// 写法2
fileName ="simpleWrite"+ System.currentTimeMillis()+".xlsx";
// 这⾥需要指定写⽤哪个class去写
ExcelWriter excelWriter = null;
try{
excelWriter = EasyExcel.write(fileName, DemoData.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
excelWriter.write(data(), writeSheet);
}finally{
// 千万别忘记finish 会帮忙关闭流
if(excelWriter != null){
excelWriter.finish();
}
}
}
解析:
第⼀种写⼊的⽅法是通过pathName(要写⼊的路径⽂件名,如果没有路径则默认为项⽬根路径下)和excel实体类的class类型来⽣成⼀个ExcelWriterBuilder构造器。通过ExcelWriterBuilder的sheet(String sheetName)来⽣成⼀个指定名称的sheet。然后通过doWrite来进⾏写⼊,doWrite会⾃动关闭流
sheet(String sheetName)默认是⽣成第⼀个sheet
第⼆种⽅法写⼊⽅法是先通过pathName(要写⼊的路径⽂件名,如果没有路径则默认为项⽬根路径下)和excel实体类的class类型来⽣成⼀个ExcelWriter,然后创建WriteSheet对象(sheet名称和数量),最后调⽤ExcelWriter的write来写⼊数据,不⾃动关闭流,所以需要⼿动关闭流。
第⼆种⽅法可以写⼊多个sheet,如下代码,就写⼊了两个sheet
ExcelWriter excelWriter = null;
try{
excelWriter = EasyExcel.write(fileName, DemoData.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet(0,"模板1").build();
WriteSheet writeSheet1 = EasyExcel.writerSheet(1,"模板2").build();
excelWriter.write(data(), writeSheet);
excelWriter.write(data(),writeSheet1);
}finally{
// 千万别忘记finish 会帮忙关闭流
if(excelWriter != null){
excelWriter.finish();
}
}
解析:写⼊多个sheet只需要创建多个WriteSheet 对象,然后调⽤多次ExcelWriter 的write即可。
运⾏测试⽅法后,可以在项⽬的根路径下看到⽣成的excel⽂件
读excel
1、创建回调,⽤于处理EasyExcel中读取的数据
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后⾥⾯⽤到s
pring可以构造⽅法传进去
public class DemoDataListener  extends AnalysisEventListener<DemoData>{
private static final Logger LOGGER = Logger(DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使⽤中可以3000条,然后清理list ,⽅便内存回收
*/
private static final int BATCH_COUNT =5;
List<DemoData> list =new ArrayList<DemoData>();
/**
* 每解析⼀条数据都会来调⽤这个⽅法
*
* @param data
*            one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context){
LOGGER.info("解析到⼀条数据:{}", JSONString(data));
list.add(data);
// 达到BATCH_COUNT了,需要去存储⼀次数据库,防⽌数据⼏万条数据在内存,容易OOM
if(list.size()>= BATCH_COUNT){
saveData();
/
/ 存储完成清理 list
list.clear();
}
}
/**
* 每个sheet的所有数据解析完成了都会来调⽤这个⽅法
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context){
/
/ 这⾥也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
LOGGER.info("所有数据解析完成!");
}
/**
* 将读取到的数据存储到数据库
*/
private void saveData(){
LOGGER.info("{}条数据,开始存储数据库!", list.size());
//存储到数据库
LOGGER.info("存储数据库成功!");
}
}
解析:EasyExcel的回调其实就是⼀个继承AnalysisEventListener抽象类的类。继承AnalysisEventListener抽象类需要实现两个⽅法,⼀个invoke()是每解析⼀条数据都要调⽤⼀次的⽅法,我们在这个⽅法⾥可以对读取到的数据进⾏处理。还有⼀个⽅法是doAfterAllAnalysed(),这个⽅法是读取完⼀个sheet所有数据后调⽤的⼀个⽅法,我们根据⾃⼰的需求来进⾏逻辑处理,如:确保最后遗留的数据也存储到数据库。
2、读取excel
@Test
public void simpleRead(){
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后⾥⾯⽤到spring可以构造⽅法传进去
// 写法1:
String fileName = Path()+"demo"+ File.separator +"demo.xlsx";
// 这⾥需要指定读⽤哪个class去读,然后读取第⼀个sheet ⽂件流会⾃动关闭
// 写法2:
fileName = Path()+"demo"+ File.separator +"demo.xlsx";
ExcelReader excelReader = null;
try{
excelReader = ad(fileName, DemoData.class,new DemoDataListener()).build();
ReadSheet readSheet = adSheet(0).build();
ExcelReader read = ad(readSheet);
}finally{
if(excelReader != null){
// 这⾥千万别忘记关闭,读的时候会创建临时⽂件,到时磁盘会崩的
excelReader.finish();
}
}
}
注:file.separator这个代表系统⽬录中的间隔符,说⽩了就是斜线,不过有时候需要双线,有时候是单线,你⽤这个静态变量就解决兼容问题了。在 UNIX 系统上,此字段的值为 ‘/’;在 Microsoft Windows 系统上,它为 ‘’。
上述例⼦都是读取⼀个sheet,有时候我们需要读取全部sheet或者多个sheet,那么我们看下⾯的例⼦
// 读取全部sheet
// 这⾥需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调⽤⼀次。然后所
有sheet都会往同⼀个DemoDataListener⾥⾯写
//读取全部sheet,使⽤doReadAll⽅法。doReadAll⽅法会⾃动关闭流,所以我们不需要⼿动关流
public void doReadAll(){
ExcelReader excelReader =build();
excelReader.finish();
}
// 读取部分sheet
ExcelReader excelReader = null;
try{
excelReader = ad(fileName).build();
// 这⾥为了简单所以注册了同样的head 和Listener ⾃⼰使⽤功能必须不同的Listener
ReadSheet readSheet1 =
ReadSheet readSheet2 =
// 这⾥注意⼀定要把sheet1 sheet2 ⼀起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
}finally{
if(excelReader != null){
// 这⾥千万别忘记关闭,读的时候会创建临时⽂件,到时磁盘会崩的
excelReader.finish();
}
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。