下面案例代码请参考:源码地址
背景
:::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**/@Slf4jpublic 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@AllArgsConstructorpublic 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 {@Resourceprivate 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进行批量导入操作监听器*/@Slf4jpublic 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;}@Overridepublic void invoke(UserExcel data, AnalysisContext context) {cachedDataList.add(data);if (cachedDataList.size() >= BATCH_COUNT) {// 存储完成清理 listcachedDataList = Lists.newArrayListWithExpectedSize(BATCH_COUNT);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {excelService.saveData(cachedDataList);}}
public interface ExcelService {/*** 存储数据* @param cachedDataList*/void saveData(List<UserExcel> cachedDataList);}
@Slf4j@Componentpublic class ExcelServiceImpl implements ExcelService {@Overridepublic void saveData(List<UserExcel> cachedDataList) {log.info("{}条数据,开始存储数据库!", cachedDataList.size());log.info("存储数据库成功!");cachedDataList.forEach(dto -> System.out.println(JSON.toJSONString(dto)));}}
