一、功能描述
EasyExcel是对Alibaba提供的ExcelPOI的一个二次开发,对其进行了再度的封装<br /> SprngBoot中集成EasyExcel功能,通过对象导入、解析到数据库;导出到本地。<br /> 官方文档:[https://www.yuque.com/easyexcel/doc/quickstart](https://www.yuque.com/easyexcel/doc/quickstart)
二、集成EasyExcel
1、导入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.0.5</version>
</dependency>
2、创建实体类(上传、下载通用)
1、EasyExcel中提供了通过对象进行Excel数据表的解析与下载<br /> 2、@ExcelProperty("字段名称")<br /> 该注解用于将Excel文件中标题进行匹配,格式要求必须遵循,不能同时使用index与name进行匹配。<br /> 3、@ExcelIngore <br /> 该注解用于忽略某些字段,比如创建时间,只需要在数据库中存在即可。
@ApiModel("数据解析实体类")
@Data
public class NucleateExcelDto {
@ExcelProperty("序号")
private Integer id;
@ExcelProperty("工作单位")
private String unitName;
@ExcelProperty("姓名")
private String name;
@ExcelProperty("身份证号码")
private String cardId;
@ExcelProperty("联系方式")
private String phone;
@ExcelProperty("最后一次核酸检测时间")
private Date lastCollectTime;
@ExcelProperty("核酸检测结果")
private String collectResult;
@ExcelIgnore
private Date createTime;
public NucleateExcelDto(){
this.createTime = new Date();
}
}
三、EasyExcel导入数据库
1、创建Listener监听器
该监听器中主要用来对文件进行读取、通过缓存容器来设定临时容器,里面可自定义调用入库方法。
需要注意的是:调用三次存储方法,需要加入非空判断逻辑(防止读取到Excel后面的空行情况)。
package com.example.epidemic.lisenter;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.example.epidemic.dto.NucleateExcelDto;
import com.example.epidemic.service.ExcelService;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
/**
* @ClassName:
* @Author: 挽风
* @Date: 2022
* @Copyright: 2022 by 挽风
* @Description:
**/
@Slf4j
public class UploadNucleateDataListener implements ReadListener<NucleateExcelDto> {
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
/**
* 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 100;
/**
* 缓存的数据
*/
private List<NucleateExcelDto> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private ExcelService excelService;
// public () {
// // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
// demoDAO = new N;
// }
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param excelService
*/
public UploadNucleateDataListener(ExcelService excelService) {
this.excelService = excelService;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(NucleateExcelDto data, AnalysisContext context) {
log.info("解析到一条数据:{}", data.toString());
// 这里需要进行非空判断,防止读取Excel格式中数据为空
if (!StrUtil.isEmpty(data.getCardId())){
cachedDataList.add(data);
}
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
excelService.saveNucleateExcelDto(cachedDataList);
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", cachedDataList.size());
// 此处用来调用service层进行数据存储
excelService.saveNucleateExcelDto(cachedDataList);
log.info("存储数据库成功!");
}
}
2、Api接口中调用上传文件
Api层中主要用来控制请求转发,直接调用Service层中的方法即可
/**
* 上传Excel数据报表接口
* @param file excel文件
* @return
*/
@PostMapping("/uploadNucleateExcel")
@ApiOperation(value = "上传报表数据Excel", notes = "上传报表文件Excel数据")
public R uploadNucleateExcel(@RequestParam("file")MultipartFile file){
return reportService.addNucleateExcelFile(file);
}
3、Service层中调用EasyExcel读取文件
ServiceImpl中实现Service接口层中的方法、、
/**
* 上传Excel数据报表接口
* @param file excel文件
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R addNucleateExcelFile(MultipartFile file){
try{
// 此处调用,传入文件流、上传Dto实体、创建上传监听器
EasyExcel.read(file.getInputStream(), NucleateExcelDto.class,
new UploadNucleateDataListener(excelService)).sheet().doRead();
}catch (Exception ignored){
throw new EpidemicException(NucleateDataEnum.UPLOAD_DATA_FAIL.getMsg(), NucleateDataEnum.UPLOAD_DATA_FAIL.getCode());
}
return R.ok();
}
四、EasyExcel导出数据库
Api层中定义Get类型的请求api接口,在其中调用Service层方法即可。
前端调用该Api层接口,Service层中直接将文件下载到浏览器。
注意:autoCloseStream(Boolean.TRUE)必须要设置为TRUE,否则会导致下载文件Excel无法打开,WPS可以打开的情况。
/**
* 下载报表数据Excel
* @param downloadExcelDto id集合参数封装
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R downloadNucleateExcel(DownloadExcelDto downloadExcelDto) {
try {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String name = "重点人群报备"; // 文件名称
String fileName = URLEncoder.encode(name, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 这里需要设置不关闭流
EasyExcel.write(response.getOutputStream(), NucleateExcelDto.class).autoCloseStream(Boolean.TRUE).sheet("模板")
.doWrite(getNucleateExcelData(downloadExcelDto.getIds(), downloadExcelDto.getUnitName()));
} catch (Exception e) {
exceptionResp(response);
}
Map<String, Object> map = MapUtils.newHashMap();
map.put("status", "true");
map.put("message", "文件下载成功");
return R.ok(map);
}