当前位置:   article > 正文

java使用EasyExcel读写及校验Excel文件_easyexcel hasnext

easyexcel hasnext

目录

0.环境说明

1. EasyExcel简介

2. 项目配置

3. EasyExcel读取Excel文件并对文件表头进行校验

4. EasyExcel写入Excel文件并根据文件内容自适应宽高


0.环境说明

  • java 1.8
  • IDEA 2022.3.1
  • EasyExcel 3.1.0

1. EasyExcel简介

        EasyExcel是阿里巴巴开源的 Excel 读取框架,其最大的特点是极低的内存消耗和极高的文件读取速度。在本文中使用 EasyExcel,实现了读取 Excel 文件并对文件表头进行校验写入 Excel文件并根据写入内容自适应调整单元格宽高

        其他更详细的信息,可以查阅官网:EasyExcel官方文档

2. 项目配置

        在开始编码之前,需要先引入 EasyExcel、lombok 和 fastjson 依赖。

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>easyexcel</artifactId>
  4. <version>3.1.0</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.projectlombok</groupId>
  8. <artifactId>lombok</artifactId>
  9. <version>1.18.8</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>com.alibaba</groupId>
  13. <artifactId>fastjson</artifactId>
  14. <version>1.2.60</version>
  15. </dependency>

3. EasyExcel 读取 Excel 文件并对文件表头进行校验

        需要读取的 Excel 文件如下:

        定义与 Excel 文件相对应的实体类。lombok 的 @Data 注解会为实体类自动生成 get 、set 以及 toString 方法。EasyExcel 的 @ExcelProperty 注解,定义了与 javaBean 字段相对应的 Excel 字段。

  1. @Data
  2. public class ExcelDTO {
  3. @ExcelProperty("姓名")
  4. private String name;
  5. @ExcelProperty("年龄")
  6. private int age;
  7. @ExcelProperty("分数")
  8. private Double score;
  9. }

         代码实现读取 Excel 文件,读取的数据被封装进 List<ExcelDTO> 中。

  1. @Test
  2. public void readExcel() {
  3. File file = new File("src/main/resources/ExcelDemo.xlsx");
  4. //默认读取Excel文件的第一个sheet
  5. List<ExcelDTO> list = EasyExcel.read(file).head(ExcelDTO.class).sheet().doReadSync();
  6. for (ExcelDTO data : list) {
  7. System.out.println(data);
  8. }
  9. }

         如果想实现在读取Excel文件的同时,对表头和文件内容进行校验,则需要定义一个 Listener 实现 EasyExcel 提供的 ReadListener 接口,实现代码如下。

  1. import com.alibaba.excel.context.AnalysisContext;
  2. import com.alibaba.excel.event.AnalysisEventListener;
  3. import java.util.Map;
  4. public class DemoDataListener extends AnalysisEventListener<ExcelDTO> {
  5. /**
  6. * 每解析一条数据,都会来调用该方法
  7. * 对所有数据进行校验,在此增加校验逻辑
  8. *
  9. * @param excelDTO
  10. * @param analysisContext
  11. */
  12. @Override
  13. public void invoke(ExcelDTO excelDTO, AnalysisContext analysisContext) {
  14. }
  15. /**
  16. * 每解析一行表头,会调用该方法
  17. *
  18. * @param headMap
  19. * @param context
  20. */
  21. @Override
  22. public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
  23. if (!headMap.containsKey(0) || !headMap.containsKey(1) || !headMap.containsKey(2)
  24. || !headMap.get(0).equals("姓名") || !headMap.get(1).equals("年龄")
  25. || !headMap.get(2).equals("分数")) {
  26. throw new RuntimeException("表头校验失败");
  27. }
  28. }
  29. /**
  30. * 所有数据都解析完成后,会调用该方法
  31. *
  32. * @param analysisContext
  33. */
  34. @Override
  35. public void doAfterAllAnalysed(AnalysisContext analysisContext) {
  36. }
  37. }

        同时,在读取Excel文件时,也需要加入 Listener ,实现的代码如下:

  1. @Test
  2. public void readExcel() {
  3. File file = new File("src/main/resources/ExcelDemo.xlsx");
  4. DemoDataListener listener = new DemoDataListener();
  5. List<ExcelDTO> list = new ArrayList<>();
  6. try {
  7. //默认读取Excel文件的第一个sheet
  8. list = EasyExcel.read(file, listener).head(ExcelDTO.class).sheet().doReadSync();
  9. } catch (Exception e) {
  10. //此处可修改为warn级别日志打印
  11. System.out.println("表头校验失败");
  12. }
  13. for (ExcelDTO data : list) {
  14. System.out.println(data);
  15. }
  16. }

4. EasyExcel 写入 Excel 文件并根据文件内容自适应宽高

        首先初始化一个 List<ExcelDTO> ,将 name 字段的内容,设置的长一些,通过如下代码可以将数据写入到 Excel 表格。

  1. @Test
  2. public void writeExcel() {
  3. List<ExcelDTO> list = new ArrayList<>();
  4. for (int i = 0; i < 10; i++) {
  5. ExcelDTO excelDTO = new ExcelDTO();
  6. excelDTO.setName("名字名字名字名字名字名字名字" + i);
  7. excelDTO.setAge(i + 30);
  8. excelDTO.setScore((double) (60 + i));
  9. list.add(excelDTO);
  10. }
  11. File file = new File("src/main/resources/WriteDemo.xlsx");
  12. EasyExcel.write("demo1.xlsx", ExcelDTO.class).sheet("模板").doWrite(list);
  13. }

        写入 Excel 文件的内容如下所示,可以看到姓名字段因为内容过长,而被隐藏了一部分,查看 Excel 文件时不便捷。

         这时,自定义一个 Config 类继承自 EasyExcel 的 AbstractColumnWidthStyleStrategy 类,达到根据内容自适应宽度的效果。

  1. import com.alibaba.excel.enums.CellDataTypeEnum;
  2. import com.alibaba.excel.metadata.Head;
  3. import com.alibaba.excel.metadata.data.CellData;
  4. import com.alibaba.excel.metadata.data.WriteCellData;
  5. import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
  6. import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
  7. import org.apache.commons.collections4.CollectionUtils;
  8. import org.apache.poi.ss.usermodel.Cell;
  9. import org.apache.poi.ss.usermodel.Sheet;
  10. import java.util.HashMap;
  11. import java.util.List;
  12. import java.util.Map;
  13. public class CustomCellWriteWeightConfig extends AbstractColumnWidthStyleStrategy {
  14. private final Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();
  15. @Override
  16. protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
  17. boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
  18. if (needSetWidth) {
  19. //computeIfAbsent方法的作用为,在通过key获取value时,若key对应的value存在,则返回value值;若不存在,则创建一个符合条件的value并返回
  20. Map<Integer, Integer> maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetNo(), k -> new HashMap<>());
  21. Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
  22. if (columnWidth >= 0) {
  23. if (columnWidth > 254) {
  24. columnWidth = 254;
  25. }
  26. Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
  27. //需要将列宽,设置为当前列最长内容的长度
  28. if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
  29. maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
  30. Sheet sheet = writeSheetHolder.getSheet();
  31. sheet.setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
  32. }
  33. }
  34. }
  35. }
  36. /**
  37. * 计算当前内容的长度,(实际就是获取当前内容的字节长度)
  38. *
  39. * @param cellDataList
  40. * @param cell
  41. * @param isHead
  42. * @return
  43. */
  44. private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
  45. if (isHead) {
  46. return cell.getStringCellValue().getBytes().length;
  47. } else {
  48. CellData<?> cellData = cellDataList.get(0);
  49. CellDataTypeEnum type = cellData.getType();
  50. if (type == null) {
  51. return -1;
  52. } else {
  53. switch (type) {
  54. case STRING:
  55. // 若存在换行符,则需要根据换行符的位置进行针对性处理
  56. int index = cellData.getStringValue().indexOf("\n");
  57. return index != -1 ?
  58. cellData.getStringValue().substring(0, index).getBytes().length + 1 : cellData.getStringValue().getBytes().length + 1;
  59. case BOOLEAN:
  60. return cellData.getBooleanValue().toString().getBytes().length;
  61. case NUMBER:
  62. return cellData.getNumberValue().toString().getBytes().length;
  63. default:
  64. return -1;
  65. }
  66. }
  67. }
  68. }
  69. }

        同样的,自定义一个 Config 类继承自 EasyExcel 的 AbstractRowHeightStyleStrategy 类,达到自适应高度的效果。

  1. import com.alibaba.excel.write.style.row.AbstractRowHeightStyleStrategy;
  2. import org.apache.poi.ss.usermodel.Cell;
  3. import org.apache.poi.ss.usermodel.CellType;
  4. import org.apache.poi.ss.usermodel.Row;
  5. import java.util.Iterator;
  6. import java.util.Objects;
  7. public class CustomCellWriteHeightConfig extends AbstractRowHeightStyleStrategy {
  8. /**
  9. * 默认高度
  10. */
  11. private static final Integer DEFAULT_HEIGHT = 300;
  12. /**
  13. * 设置表头的高度
  14. *
  15. * @param row
  16. * @param relativeRowIndex
  17. */
  18. @Override
  19. protected void setHeadColumnHeight(Row row, int relativeRowIndex) {
  20. }
  21. /**
  22. * 设置内容的高度
  23. *
  24. * @param row
  25. * @param relativeRowIndex
  26. */
  27. @Override
  28. protected void setContentColumnHeight(Row row, int relativeRowIndex) {
  29. Iterator<Cell> cellIterator = row.cellIterator();
  30. if (!cellIterator.hasNext()) {
  31. return;
  32. }
  33. // 默认为 1行高度
  34. int maxHeight = 1;
  35. while (cellIterator.hasNext()) {
  36. Cell cell = cellIterator.next();
  37. if (Objects.requireNonNull(cell.getCellTypeEnum()) == CellType.STRING) {
  38. if (cell.getStringCellValue().contains("\n")) {
  39. int length = cell.getStringCellValue().split("\n").length;
  40. maxHeight = Math.max(maxHeight, length);
  41. }
  42. }
  43. }
  44. row.setHeight((short) (maxHeight * DEFAULT_HEIGHT));
  45. }
  46. }

        在写入 Excel 文件时,将自定义的两个 Config 类注册到 EasyExcel 中。

  1. @Test
  2. public void writeExcel() {
  3. List<ExcelDTO> list = new ArrayList<>();
  4. for (int i = 0; i < 10; i++) {
  5. ExcelDTO excelDTO = new ExcelDTO();
  6. excelDTO.setName("名字名字名字名字名字名字名字" + i);
  7. excelDTO.setAge(i + 30);
  8. excelDTO.setScore((double) (60 + i));
  9. list.add(excelDTO);
  10. }
  11. File file = new File("src/main/resources/WriteDemo.xlsx");
  12. EasyExcel.write("demo1.xlsx", ExcelDTO.class).sheet("模板").
  13. registerWriteHandler(new CustomCellWriteWeightConfig()).registerWriteHandler(new CustomCellWriteHeightConfig()).doWrite(list);
  14. }

        写入的 Excel 文件如图,可以看到宽度已经经过自适应调整了。

 

关注公众号打代码的苏比特,获取java面试百问百答面试高频算法题~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/编程变革者/article/detail/61033
推荐阅读
相关标签
  

闽ICP备14008679号