一、功能描述
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("数据解析实体类")@Datapublic 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;@ExcelIgnoreprivate 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:**/@Slf4jpublic 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*/@Overridepublic void invoke(NucleateExcelDto data, AnalysisContext context) {log.info("解析到一条数据:{}", data.toString());// 这里需要进行非空判断,防止读取Excel格式中数据为空if (!StrUtil.isEmpty(data.getCardId())){cachedDataList.add(data);}// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (cachedDataList.size() >= BATCH_COUNT) {saveData();// 存储完成清理 listcachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic 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);}
