下面案例代码请参考:源码地址

背景

:::tips 后台管理系统有很多处理excel导入,导出的功能.(比如,批量新增功能),这时候就需要有工具来处理excel文件.常见的有Apache POI以及Alibaba EasyExcel.下面针对easyexcel进行工具类的封装 :::

依赖

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>easyexcel</artifactId>
  4. <version>2.1.6</version>
  5. </dependency>

工具类

文件地址

  1. /**
  2. * 太阳当空照,花儿对我笑
  3. * <p>
  4. * Create by M ChangKe 2022/7/6 11:56
  5. **/
  6. @Slf4j
  7. public class EasyExcelUtil {
  8. /**
  9. * 同步无模型读(指定sheet和表头占的行数)
  10. *
  11. * @param inputStream
  12. * @param sheetNo sheet页号,从0开始
  13. * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
  14. */
  15. public static List<Map<Integer, String>> syncRead(InputStream inputStream, Integer sheetNo, Integer headRowNum) {
  16. return EasyExcelFactory
  17. .read(inputStream)
  18. .sheet(sheetNo)
  19. .headRowNumber(headRowNum)
  20. .doReadSync();
  21. }
  22. /**
  23. * 同步无模型读(指定sheet和表头占的行数)
  24. *
  25. * @param file
  26. * @param sheetNo sheet页号,从0开始
  27. * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
  28. */
  29. public static List<Map<Integer, String>> syncRead(File file, Integer sheetNo, Integer headRowNum) {
  30. return EasyExcelFactory.read(file).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();
  31. }
  32. /**
  33. * 同步按模型读(指定sheet和表头占的行数)
  34. *
  35. * @param inputStream
  36. * @param clazz 模型的类类型(excel数据会按该类型转换成对象)
  37. * @param sheetNo sheet页号,从0开始
  38. * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
  39. */
  40. public static <T> List<T> syncReadModel(InputStream inputStream, Class<T> clazz, Integer sheetNo, Integer headRowNum) {
  41. return EasyExcelFactory
  42. .read(inputStream)
  43. .sheet(sheetNo)
  44. .headRowNumber(headRowNum)
  45. .head(clazz)
  46. .doReadSync();
  47. }
  48. /**
  49. * 同步按模型读(指定sheet和表头占的行数)
  50. *
  51. * @param file
  52. * @param clazz 模型的类类型(excel数据会按该类型转换成对象)
  53. * @param sheetNo sheet页号,从0开始
  54. * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
  55. */
  56. public static <T> List<T> syncReadModel(File file, Class<T> clazz, Integer sheetNo, Integer headRowNum) {
  57. return EasyExcelFactory
  58. .read(file)
  59. .sheet(sheetNo)
  60. .headRowNumber(headRowNum)
  61. .head(clazz).doReadSync();
  62. }
  63. /**
  64. * 异步无模型读(指定sheet和表头占的行数)
  65. *
  66. * @param inputStream
  67. * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
  68. */
  69. public static void asyncRead(InputStream inputStream, ReadListener<T> excelListener) {
  70. EasyExcelUtil.asyncRead(inputStream, excelListener, 0, 1);
  71. }
  72. /**
  73. * 异步无模型读(指定sheet和表头占的行数)
  74. *
  75. * @param inputStream
  76. * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
  77. * @param sheetNo sheet页号,从0开始
  78. * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
  79. */
  80. public static void asyncRead(InputStream inputStream, ReadListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
  81. EasyExcelFactory
  82. .read(inputStream, excelListener)
  83. .sheet(sheetNo)
  84. .headRowNumber(headRowNum)
  85. .doRead();
  86. }
  87. /**
  88. * 异步无模型读(指定sheet和表头占的行数)
  89. *
  90. * @param file
  91. * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
  92. * @param sheetNo sheet页号,从0开始
  93. * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
  94. */
  95. public static void asyncRead(File file, ReadListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
  96. EasyExcelFactory.read(file, excelListener)
  97. .sheet(sheetNo)
  98. .headRowNumber(headRowNum)
  99. .doRead();
  100. }
  101. /**
  102. * 异步按模型读取, sheet页号,从0开始,读取数据(排除第一行表头)
  103. *
  104. * @param inputStream
  105. * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
  106. * @param clazz 模型的类类型(excel数据会按该类型转换成对象)
  107. * @param <T>
  108. */
  109. public static <T> void asyncReadModel(InputStream inputStream, ReadListener<T> excelListener, Class<T> clazz) {
  110. EasyExcelUtil.asyncReadModel(inputStream, excelListener, clazz, 0, 1);
  111. }
  112. /**
  113. * 异步按模型读取
  114. *
  115. * @param inputStream
  116. * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
  117. * @param clazz 模型的类类型(excel数据会按该类型转换成对象)
  118. * @param sheetNo sheet页号,从0开始
  119. * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
  120. */
  121. public static <T> void asyncReadModel(InputStream inputStream, ReadListener<T> excelListener, Class<T> clazz, Integer sheetNo, Integer headRowNum) {
  122. EasyExcelFactory.read(inputStream, clazz, excelListener)
  123. .sheet(sheetNo)
  124. .headRowNumber(headRowNum)
  125. .doRead();
  126. }
  127. /**
  128. * 异步按模型读取
  129. *
  130. * @param file
  131. * @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
  132. * @param clazz 模型的类类型(excel数据会按该类型转换成对象)
  133. * @param sheetNo sheet页号,从0开始
  134. * @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
  135. */
  136. public static <T> void asyncReadModel(File file, ReadListener<T> excelListener, Class<T> clazz, Integer sheetNo, Integer headRowNum) {
  137. EasyExcelFactory.read(file, clazz, excelListener)
  138. .sheet(sheetNo)
  139. .headRowNumber(headRowNum)
  140. .doRead();
  141. }
  142. /**
  143. * 导出Excel到文件
  144. *
  145. * @param filePath 文件地址
  146. * @param clazz 泛型类
  147. * @param data 待导出数据列表
  148. * @param sheetName sheetName
  149. */
  150. public static <T> void write(String filePath, Class<T> clazz, List<T> data, String sheetName) {
  151. EasyExcel.write(filePath, clazz)
  152. .sheet(sheetName)
  153. .doWrite(data);
  154. }
  155. /**
  156. * 导出excel到response响应流
  157. *
  158. * @param response response响应流
  159. * @param clazz 泛型类
  160. * @param data 待导出数据列表
  161. * @param exportFileName 导出的excel文件名
  162. * @param sheetName sheetName
  163. * @param <T> 泛型
  164. */
  165. public static <T> void writeWeb(HttpServletResponse response, Class<T> clazz, List<T> data, String exportFileName, String sheetName) {
  166. response.setContentType("application/vnd.ms-excel");
  167. response.setCharacterEncoding("utf-8");
  168. try (ServletOutputStream outputStream = response.getOutputStream()) {
  169. // 这里URLEncoder.encode可以防止中文乱码
  170. String fileName = URLEncoder.encode(exportFileName, "UTF-8");
  171. response.setHeader("Content-disposition", "attachment;filename=" + fileName + ExcelTypeEnum.XLSX.getValue());
  172. EasyExcel.write(outputStream, clazz)
  173. .sheet(sheetName)
  174. .doWrite(data);
  175. } catch (Exception e) {
  176. e.printStackTrace();
  177. }
  178. }
  179. }

案例

待读取的Excel模板如下:
image.png
根据上述模板,创建UserExcel 模板类用于excel的读取与导出

  1. @Data
  2. @NoArgsConstructor
  3. @AllArgsConstructor
  4. public class UserExcel {
  5. @ExcelProperty(value = "编号",index = 0)
  6. private String serialNo;
  7. @ExcelProperty(value = "地址",index = 1)
  8. private String quickCodeUrl;
  9. }

UploadDownController控制器

  1. @Slf4j
  2. @RestController
  3. @Api(value = "UploadDownController", tags = "上传下载教程")
  4. public class UploadDownController {
  5. @Resource
  6. private ExcelService excelService;
  7. @ApiOperation(value = "异步读取excel进行入库操作", notes = "异步读取excel进行入库操作", consumes = "multipart/form-data")
  8. @PostMapping("read")
  9. public ApiResponse<String> read(@RequestParam("file") MultipartFile file) {
  10. try (
  11. InputStream inputStream = file.getInputStream()
  12. ) {
  13. EasyExcelUtil.asyncReadModel(inputStream, new MyExcelReadListener(excelService), UserExcel.class);
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. return ApiResponse.ok("my name is:" + file.getName());
  18. }
  19. }

MyExcelReadListener

  1. /**
  2. * 读取用户excel进行批量导入操作监听器
  3. */
  4. @Slf4j
  5. public class MyExcelReadListener extends AnalysisEventListener<UserExcel> {
  6. /**
  7. * 单次缓存的数据量
  8. */
  9. public static final int BATCH_COUNT = 100;
  10. /**
  11. * 临时存储
  12. */
  13. private List<UserExcel> cachedDataList = Lists.newArrayListWithExpectedSize(BATCH_COUNT);
  14. /**
  15. *
  16. */
  17. private ExcelService excelService;
  18. public MyExcelReadListener(ExcelService excelService) {
  19. this.excelService = excelService;
  20. }
  21. @Override
  22. public void invoke(UserExcel data, AnalysisContext context) {
  23. cachedDataList.add(data);
  24. if (cachedDataList.size() >= BATCH_COUNT) {
  25. // 存储完成清理 list
  26. cachedDataList = Lists.newArrayListWithExpectedSize(BATCH_COUNT);
  27. }
  28. }
  29. @Override
  30. public void doAfterAllAnalysed(AnalysisContext context) {
  31. excelService.saveData(cachedDataList);
  32. }
  33. }
  1. public interface ExcelService {
  2. /**
  3. * 存储数据
  4. * @param cachedDataList
  5. */
  6. void saveData(List<UserExcel> cachedDataList);
  7. }
  1. @Slf4j
  2. @Component
  3. public class ExcelServiceImpl implements ExcelService {
  4. @Override
  5. public void saveData(List<UserExcel> cachedDataList) {
  6. log.info("{}条数据,开始存储数据库!", cachedDataList.size());
  7. log.info("存储数据库成功!");
  8. cachedDataList.forEach(dto -> System.out.println(JSON.toJSONString(dto)));
  9. }
  10. }