• EasyExcel3.1.1版本上传文件忽略列头大小写


    1、背景

    项目中使用easyExcel3.1.1版本实现上传下载功能,相关数据DTO

    @ExcelProperty(value = "dealer_gssn_id")

    形式规定其每一列的名称,这样的话easyExcel会完全匹配对应的列名,即用户上传文件时,列名写成Dealer_gssn_id,那么对应的DTO不会给对应字段赋值。

    现在客户需要实现此项需求,忽略列名大小写,延伸出来以下内容。

    2、调研

    最开始以为easyExcel有现成的一些方法或者重写类能实现此项功能,但是后来查看源码,发现没办法重写。具体的debug流程不再此细说,主要看一下DefaultAnalysisEventProcessor类:

    该类中dealData方法,调用buildHead方法,在该方法中141行使用的equals方法用来比较DTO中value名字与excel上传的列名是否匹配,然而这个类的调用方式是new出来的,这就代表即使我们重写了DefaultAnalysisEventProcessor 类,也没办法执行到我们重写的方法,所以这条路走不通了。

    除此之外就只能靠我们自己发散思维去想想该怎么做了。

    通过打断点看数据,我们其实能在重写AnalysisEventListener的类中看到invokeHeadMap类中是所有的列头数据,invoke方法中既有列头数据,也有具体的值信息,但是怎么和我们定义的DTO做绑定,这也是个麻烦的事情,可以做,但是想想就比较复杂,可能需要写一大堆的if else,代码也不美观,特别是DTO中字段六七十个,而且不止这一个文件要改,这样做起来麻烦且费时。

    那就接着想想其他可能性。

    比如在我们上传文件时,在处理数据之前写个公共的util呢?这个util功能会把excel列头拿出来都转化为小写,再重新生成一个MultipartFile文件对象呢?这样,每个上传excel功能我们手动调用一下是不是代码侵入量小一些呢?

    所以接下来开始硬刚这部分操作,修修补补,起码能实现上述需求:

    这个util可以直接使用,但是每个人场景不同可能需要略微修改,这个util只针对sheet 1中简单的区分列头与数据,比较复杂的excel还需要按实际情况修改:

    1. import com.alibaba.excel.EasyExcel;
    2. import com.alibaba.excel.ExcelWriter;
    3. import com.alibaba.excel.context.AnalysisContext;
    4. import com.alibaba.excel.event.AnalysisEventListener;
    5. import com.alibaba.excel.support.ExcelTypeEnum;
    6. import com.alibaba.excel.write.metadata.WriteSheet;
    7. import org.springframework.mock.web.MockMultipartFile;
    8. import org.springframework.util.ObjectUtils;
    9. import org.springframework.web.multipart.MultipartFile;
    10. import java.io.ByteArrayInputStream;
    11. import java.io.ByteArrayOutputStream;
    12. import java.io.InputStream;
    13. import java.util.ArrayList;
    14. import java.util.List;
    15. import java.util.Map;
    16. public class FileUtilsForHeaderLowerCase {
    17. public static MultipartFile convertToLowerCase(MultipartFile file) throws Exception {
    18. // 临时存储读取到的数据
    19. List<List<String>> headerData = new ArrayList<>();
    20. List<List<String>> rowData = new ArrayList<>();
    21. ExcelTypeEnum excelTypeEnum = null;
    22. if (file.getOriginalFilename().toLowerCase().endsWith("csv")) {
    23. excelTypeEnum = ExcelTypeEnum.CSV;
    24. }else if(file.getOriginalFilename().toLowerCase().endsWith("xlsx")) {
    25. excelTypeEnum = ExcelTypeEnum.XLSX;
    26. }else if(file.getOriginalFilename().toLowerCase().endsWith("xls")){
    27. excelTypeEnum = ExcelTypeEnum.XLS;
    28. }
    29. // 读取原始文件内容
    30. try (InputStream inputStream = file.getInputStream()) {
    31. // 使用EasyExcel读取原始文件数据
    32. EasyExcel.read(inputStream, new AnalysisEventListener<>() {
    33. @Override
    34. public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
    35. // 转换为小写并存储列头
    36. List<String> headers = new ArrayList<>();
    37. headMap.forEach((k, v) -> headers.add(ObjectUtils.isEmpty(v) ? "" : v.toLowerCase()));
    38. headerData.add(headers);
    39. }
    40. @Override
    41. public void invoke(Object data1, AnalysisContext context) {
    42. Map<Integer, String> stringData = (Map<Integer, String>) data1;
    43. List<String> list = new ArrayList<>();
    44. stringData.forEach((key, value) -> list.add(ObjectUtils.isEmpty(value) ? "" : value));
    45. rowData.add(list);
    46. }
    47. @Override
    48. public void doAfterAllAnalysed(AnalysisContext context) {
    49. // 所有数据解析完成后调用此方法,这里不做什么
    50. }
    51. }).excelType(excelTypeEnum).sheet().doRead();
    52. // 创建一个字节数组输出流来存储Excel内容
    53. ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    54. // 使用EasyExcel创建ExcelWriter
    55. try (ExcelWriter excelWriter = EasyExcel.write(byteArrayOutputStream).excelType(excelTypeEnum).build()) {
    56. // 创建写入的sheet页
    57. WriteSheet writeSheet = EasyExcel.writerSheet().build();
    58. // 写入表头
    59. if (!headerData.isEmpty()) {
    60. excelWriter.write(headerData, writeSheet);
    61. }
    62. // 写入数据
    63. if (!rowData.isEmpty()) {
    64. excelWriter.write(rowData, writeSheet);
    65. }
    66. }
    67. // 将字节数组输出流转换为输入流
    68. InputStream newInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    69. // 使用转换后的输入流创建一个新的MultipartFile
    70. return new MockMultipartFile(
    71. "file",
    72. file.getOriginalFilename(),
    73. file.getContentType(),
    74. newInputStream
    75. );
    76. }
    77. }
    78. }

  • 相关阅读:
    JSP setProperty动作
    30岁了,还能转行做测试/开发程序员吗?下定决心开始干......
    MySQL基础-事务
    griffin batch模式代码简单分析
    SoftLabel
    Codeforces Round 908 (Div. 2)视频详解
    C++面向对象核心编程必备知识点之----堆区
    ROS知识点——生成点云,发布、订阅ROS点云话题
    天线设计中的负载牵引
    C# 提取Word中插入的多媒体文件(视频、音频)
  • 原文地址:https://blog.csdn.net/qq_43137849/article/details/136437312