下面案例代码请参考:源码地址
背景
:::tips 后台管理系统有很多处理excel导入,导出的功能.(比如,批量新增功能),这时候就需要有工具来处理excel文件.常见的有Apache POI以及Alibaba EasyExcel.下面针对easyexcel进行工具类的封装 :::
依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.6</version>
</dependency>
工具类
/**
* 太阳当空照,花儿对我笑
* <p>
* Create by M ChangKe 2022/7/6 11:56
**/
@Slf4j
public class EasyExcelUtil {
/**
* 同步无模型读(指定sheet和表头占的行数)
*
* @param inputStream
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static List<Map<Integer, String>> syncRead(InputStream inputStream, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory
.read(inputStream)
.sheet(sheetNo)
.headRowNumber(headRowNum)
.doReadSync();
}
/**
* 同步无模型读(指定sheet和表头占的行数)
*
* @param file
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static List<Map<Integer, String>> syncRead(File file, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(file).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();
}
/**
* 同步按模型读(指定sheet和表头占的行数)
*
* @param inputStream
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static <T> List<T> syncReadModel(InputStream inputStream, Class<T> clazz, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory
.read(inputStream)
.sheet(sheetNo)
.headRowNumber(headRowNum)
.head(clazz)
.doReadSync();
}
/**
* 同步按模型读(指定sheet和表头占的行数)
*
* @param file
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static <T> List<T> syncReadModel(File file, Class<T> clazz, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory
.read(file)
.sheet(sheetNo)
.headRowNumber(headRowNum)
.head(clazz).doReadSync();
}
/**
* 异步无模型读(指定sheet和表头占的行数)
*
* @param inputStream
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
*/
public static void asyncRead(InputStream inputStream, ReadListener<T> excelListener) {
EasyExcelUtil.asyncRead(inputStream, excelListener, 0, 1);
}
/**
* 异步无模型读(指定sheet和表头占的行数)
*
* @param inputStream
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static void asyncRead(InputStream inputStream, ReadListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory
.read(inputStream, excelListener)
.sheet(sheetNo)
.headRowNumber(headRowNum)
.doRead();
}
/**
* 异步无模型读(指定sheet和表头占的行数)
*
* @param file
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static void asyncRead(File file, ReadListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(file, excelListener)
.sheet(sheetNo)
.headRowNumber(headRowNum)
.doRead();
}
/**
* 异步按模型读取, sheet页号,从0开始,读取数据(排除第一行表头)
*
* @param inputStream
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param <T>
*/
public static <T> void asyncReadModel(InputStream inputStream, ReadListener<T> excelListener, Class<T> clazz) {
EasyExcelUtil.asyncReadModel(inputStream, excelListener, clazz, 0, 1);
}
/**
* 异步按模型读取
*
* @param inputStream
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static <T> void asyncReadModel(InputStream inputStream, ReadListener<T> excelListener, Class<T> clazz, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(inputStream, clazz, excelListener)
.sheet(sheetNo)
.headRowNumber(headRowNum)
.doRead();
}
/**
* 异步按模型读取
*
* @param file
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static <T> void asyncReadModel(File file, ReadListener<T> excelListener, Class<T> clazz, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(file, clazz, excelListener)
.sheet(sheetNo)
.headRowNumber(headRowNum)
.doRead();
}
/**
* 导出Excel到文件
*
* @param filePath 文件地址
* @param clazz 泛型类
* @param data 待导出数据列表
* @param sheetName sheetName
*/
public static <T> void write(String filePath, Class<T> clazz, List<T> data, String sheetName) {
EasyExcel.write(filePath, clazz)
.sheet(sheetName)
.doWrite(data);
}
/**
* 导出excel到response响应流
*
* @param response response响应流
* @param clazz 泛型类
* @param data 待导出数据列表
* @param exportFileName 导出的excel文件名
* @param sheetName sheetName
* @param <T> 泛型
*/
public static <T> void writeWeb(HttpServletResponse response, Class<T> clazz, List<T> data, String exportFileName, String sheetName) {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
try (ServletOutputStream outputStream = response.getOutputStream()) {
// 这里URLEncoder.encode可以防止中文乱码
String fileName = URLEncoder.encode(exportFileName, "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ExcelTypeEnum.XLSX.getValue());
EasyExcel.write(outputStream, clazz)
.sheet(sheetName)
.doWrite(data);
} catch (Exception e) {
e.printStackTrace();
}
}
}
案例
待读取的Excel模板如下:
根据上述模板,创建UserExcel 模板类用于excel的读取与导出
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserExcel {
@ExcelProperty(value = "编号",index = 0)
private String serialNo;
@ExcelProperty(value = "地址",index = 1)
private String quickCodeUrl;
}
UploadDownController控制器
@Slf4j
@RestController
@Api(value = "UploadDownController", tags = "上传下载教程")
public class UploadDownController {
@Resource
private ExcelService excelService;
@ApiOperation(value = "异步读取excel进行入库操作", notes = "异步读取excel进行入库操作", consumes = "multipart/form-data")
@PostMapping("read")
public ApiResponse<String> read(@RequestParam("file") MultipartFile file) {
try (
InputStream inputStream = file.getInputStream()
) {
EasyExcelUtil.asyncReadModel(inputStream, new MyExcelReadListener(excelService), UserExcel.class);
} catch (Exception e) {
e.printStackTrace();
}
return ApiResponse.ok("my name is:" + file.getName());
}
}
MyExcelReadListener
/**
* 读取用户excel进行批量导入操作监听器
*/
@Slf4j
public class MyExcelReadListener extends AnalysisEventListener<UserExcel> {
/**
* 单次缓存的数据量
*/
public static final int BATCH_COUNT = 100;
/**
* 临时存储
*/
private List<UserExcel> cachedDataList = Lists.newArrayListWithExpectedSize(BATCH_COUNT);
/**
*
*/
private ExcelService excelService;
public MyExcelReadListener(ExcelService excelService) {
this.excelService = excelService;
}
@Override
public void invoke(UserExcel data, AnalysisContext context) {
cachedDataList.add(data);
if (cachedDataList.size() >= BATCH_COUNT) {
// 存储完成清理 list
cachedDataList = Lists.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
excelService.saveData(cachedDataList);
}
}
public interface ExcelService {
/**
* 存储数据
* @param cachedDataList
*/
void saveData(List<UserExcel> cachedDataList);
}
@Slf4j
@Component
public class ExcelServiceImpl implements ExcelService {
@Override
public void saveData(List<UserExcel> cachedDataList) {
log.info("{}条数据,开始存储数据库!", cachedDataList.size());
log.info("存储数据库成功!");
cachedDataList.forEach(dto -> System.out.println(JSON.toJSONString(dto)));
}
}