上一篇写了一个SpringBoot整合poi实现excel导入导出的,但是使用poi会有一些问题,这时阿里带着EasyExcel来了。
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模式。在上层做了模型转换的封装,让使用者更加简单方便。
这个整合EasyExcel的demo是在上一篇整合poi的基础上改进的,这里给个传送门SpringBoot实现Excel文件上传至数据库及下载数据库数据为Excel
源码已上传至码云
下面贴出修改的地方
先改一下依赖pom.xml
com.alibaba
easyexcel
1.1.2-beta5
org.apache.poi
poi
3.17
org.apache.poi
poi-ooxml
3.17
上传excl
上传excl并插入到数据库
数据导出poi
数据导出easyexcel
导出
user实体类
导出 Excel 时,若需要表头,那么相应的实体类需要继承 BaseRowModel,并加入 @ExcelProperty(value = “id”, index = 0) 注解。其中 value 代表在导出 Excel 时,该字段对应的表头名称;index 代表该字段对应的表头位置(从0开始)
package com.thz.excl_upload.entity;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;
import java.io.Serializable;
import java.util.Date;
/**
* (User)实体类
*
* @author makejava
* @since 2020-07-22 09:57:53
*/
public class User extends BaseRowModel implements Serializable {
private static final long serialVersionUID = -75075031034829113L;
@ExcelProperty(value = {"ID"}, index = 0)
private Integer id;
@ExcelProperty(value = {"neme"}, index = 1)
private String name;
@ExcelProperty(value = {"性别"}, index = 2)
private String sex;
@ExcelProperty(value = {"创建时间"}, index = 3)
private Date createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
UserController中添加下面的方法
@RequestMapping("UserExcelDownloadsEasyExcel")
public void UserExcelDownloadsEasyExcel(HttpServletResponse response) throws IOException {
ExcelWriter writer = EasyExcelFactory.getWriter(response.getOutputStream());
// 写仅有一个 Sheet 的 Excel 文件, 此场景较为通用
Sheet sheet1 = new Sheet(1, 0, User.class);
// 第一个 sheet 名称
sheet1.setSheetName("第一个sheet");
// 写数据到 Writer 上下文中
// 入参1: 数据库查询的数据list集合
// 入参2: 要写入的目标 sheet
writer.write(userService.queryAll(new User()), sheet1);
// 将上下文中的最终 outputStream 写入到指定文件中
response.setContentType("application/octet-stream");
String fileName = "userinf" + ".xls";
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
response.flushBuffer();
writer.finish();
}
导入
因为EasyExcel是依据实体类中的@ExcelProperty注解来解析Excel表中每一列的数据的,如果说上传表中数据和数据库中字段是对应的 ,那我们可以继续使用上面的实体类。但是通常业务传过来的表可能是只有一些关键字段,这时候我们还想进行上传需要修改原先User实体的注解与上传表一致,可这样导出的数据就不全了。为了解决这个问题需要再创建一个专门的上传UserUpLoad实体类。
我的表是上面这样的,所以新创建了如下UserUpLoad实体
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;
import java.io.Serializable;
import java.util.Date;
/**
* (User)实体类
*
* @author makejava
* @since 2020-07-22 09:57:53
*/
public class UserUpLoad extends BaseRowModel implements Serializable {
private static final long serialVersionUID = -75075031034829113L;
private Integer id;
@ExcelProperty(value = {"neme"}, index = 0)
private String name;
@ExcelProperty(value = {"性别"}, index = 1)
private String sex;
private Date createTime;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
UserUpLoadDao
/**
* (UserUpLoad)表数据库访问层
*
* @author makejava
* @since 2020-07-27 09:57:53
*/
@Mapper
@Component
public interface UserUpLoadDao {
/**
* 新增数据
*
* @param user 实例对象
* @return 影响行数
*/
int insert(UserUpLoad user);
}
UserUpLoadDao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
>
>
>
>
>
>
>
<!--新增所有列-->
insert into excl_test.user(name, sex, create_time)
values (#{name}, #{sex}, #{createTime})
>
实体类的准备工作做完了,下面修改一下Controller
@RequestMapping(value = "/uploadEasyExcl")
public @ResponseBody
Map<String ,Object> uploadEasyExcl(HttpServletRequest request, @RequestParam("file") MultipartFile file) throws IOException {
Map<String ,Object> result = new HashMap<>();
userService.saveUser(file);
return result;
}
UserServiceImpl
@Override
public void saveUser(MultipartFile file) throws IOException {
if(!file.getOriginalFilename().equals("上传测试.xls") && !file.getOriginalFilename().equals("上传测试.xlsx") ){
return;
}
InputStream inputStream = new BufferedInputStream(file.getInputStream());
//实例化实现了AnalysisEventListener接口的类
ExcelListener excelListener = new ExcelListener(userUpLoadDao);
ExcelReader reader = new ExcelReader(inputStream,null,excelListener);
//读取信息
reader.read(new Sheet(1,1,UserUpLoad.class));
}
ExcelListener
package com.thz.excl_upload.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.thz.excl_upload.dao.UserUpLoadDao;
import com.thz.excl_upload.entity.UserUpLoad;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class ExcelListener extends AnalysisEventListener<UserUpLoad> {
private List<UserUpLoad> datas = new ArrayList<>();
private static final int BATCH_COUNT = 3000;
private UserUpLoadDao userUpLoadDao;
public ExcelListener(UserUpLoadDao userUpLoadDao){
this.userUpLoadDao = userUpLoadDao;
}
@Override
public void invoke(UserUpLoad user, AnalysisContext analysisContext) {
//数据存储到datas,供批量处理,或后续自己业务逻辑处理。
datas.add(user);
//达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if(datas.size() >= BATCH_COUNT){
saveData();
// 存储完成清理datas
datas.clear();
}
}
private void saveData() {
for(UserUpLoad user : datas){
user.setCreateTime(new Date());
this.userUpLoadDao.insert(user);
}
}
public List<UserUpLoad> getDatas() {
return datas;
}
public void setDatas(List<UserUpLoad> datas) {
this.datas = datas;
}
/**
* 所有数据解析完成了 都会来调用
*/
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
saveData();//确保所有数据都能入库
}
}
参考Springboot整合easyExcel导入导出Excel