# SpringBoot集成Excel
**Repository Path**: jianml/excel
## Basic Information
- **Project Name**: SpringBoot集成Excel
- **Description**: SpringBoot集成Excel
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2020-01-06
- **Last Updated**: 2021-03-06
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# SpringBoot集成Excel
## 简介
EasyExcel是阿里开源的解析excel的工具,可以把它看作对poi的优化版
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到KB级别,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便
> 参考:https://github.com/alibaba/easyexcel
## 引入依赖
```xml
com.alibaba
easyexcel
2.1.6
```
## 创建实体类
注意:实体类使用Lombok生成构造方法时,添加了有参构造注解之后一定要添加无参构建注解,否则会抛出异常
```java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@ExcelProperty("姓名")
private String username;
@ExcelProperty("密码")
private String password;
@ExcelProperty(value = "性别", converter = SexConverter.class)
private Integer sex;
@ExcelProperty("出生日期")
private Date birthday;
@ExcelIgnore
private String address;
}
```
### 注解
- `ExcelProperty` 指定当前字段对应excel中的那一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。
- `ExcelIgnore` 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
- `DateTimeFormat` 日期转换,用`String`去接收excel日期格式的数据会调用这个注解。里面的`value`参照`java.text.SimpleDateFormat`
- `NumberFormat` 数字转换,用`String`去接收excel数字格式的数据会调用这个注解。里面的`value`参照`java.text.DecimalFormat`
PS:对于` @ExcelProperty(value = "性别", converter = SexConverter.class)`有疑惑的话后面有一节会专门讲转换器的使用,请仔细往下继续看
## Excel导入
### 编写excel的监听器
有个很重要的点` ImportExcelListener `不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
```java
@Slf4j
public class ImportExcelListener extends AnalysisEventListener {
private UserService userService;
private List list = new ArrayList<>();
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
public ImportExcelListener(UserService userService){
this.userService = userService;
}
/**
* 这个每一条数据解析都会来调用
*/
@Override
public void invoke(User data, AnalysisContext analysisContext) {
log.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
if(list.size() >= BATCH_COUNT){
saveData();
// 清理掉已存储的数据
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*/
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
if(list.size() > 0) saveData();
log.info("所有数据解析完成");
}
private void saveData(){
log.info("{}条数据,开始存储数据库", list.size());
userService.saveData(list);
log.info("存储数据库成功");
}
}
```
### 导入excel代码
```java
/**
* 导入Excel数据(默认异步读取excel)
*/
@PostMapping("/import")
public String importExcels(@RequestParam("file") MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), User.class, new ImportExcelListener(userService)).sheet().doRead();
return "导入成功";
}
```
## Excel导出
### 获取数据
由于我们没有连接数据库,所以我们直接用代码直接生成数据
```java
@Override
public List getData() {
// 模拟从数据库中取数据
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
List list = new ArrayList<>();
try{
list.add(new User("wujian", "123456", 0, format.parse("1994-10-13"), "湖南省"));
list.add(new User("李四", "111111", 1, format.parse("1992-11-16"), "江苏省"));
list.add(new User("王五", "222222", 2, format.parse("1991-04-03"), "北京市"));
}catch (Exception e){
e.printStackTrace();
}
return list;
}
```
### 导出excel代码
```java
/**
* 导出 Excel
*/
@PostMapping("/export")
public void export(HttpServletResponse response) throws IOException{
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("测试", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), User.class).sheet("模板").doWrite(userService.getData());
}
```
## 自定义转换器
这是最基本的数据读写,我们的业务数据通常不可能这么简单,有时甚至需要将其转换为程序可读的数据
比如 Excel 中新增「性别」列,其性别为男/女/未知,我们需要将 Excel 中的性别信息转换成程序信息: 「0: 男;1:女;2:未知」
### 首先在 User 实体中添加成员变量 `sex`
```java
@ExcelProperty(value = "性别", converter = SexConverter.class)
private Integer sex;
```
EasyExcel 支持我们自定义 converter,将 excel 的内容转换为我们程序需要的信息,这里新建 `SexConverter`,用来转换性别信息
```java
public class SexConverter implements Converter {
private static final String MALE = "男";
private static final String FEMALE = "女";
private static final String UNKNOW = "未知";
@Override
public Class supportJavaTypeKey() {
return Integer.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 这里读的时候会调用
*/
@Override
public Integer convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
String value = cellData.getStringValue();
if(MALE.equals(value)) return 0;
else if(FEMALE.equals(value)) return 1;
else return 2;
}
/**
* 这里是写的时候会调用
*/
@Override
public CellData convertToExcelData(Integer integer, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
String value = UNKNOW;
if(0 == integer) value = MALE;
else if(1 == integer) value = FEMALE;
return new CellData(value);
}
}
```
## 封装优化
### 封装 解析监听类 (也可以不封装,直接继承AnalysisEventListener 方法)
```java
@Slf4j
public abstract class ExcelListener extends AnalysisEventListener {
public static final int BATCH_COUNT = 1000;
private List