掘金 后端 ( ) • 2024-03-28 10:25

前言

在使用 Easy Excel做导入数据时,需求是将所有导入的参数去除空格(这里做其他处理也都可以),总不能在读取数据之后,然后在业务层进行数据的处理吧,这样太 low 也太麻烦了。easyExcel 就提供了一个自定义转换器,就是使用了这个玩意,踩了一个坑。

正文

直接上代码

简单读对象

@ApiModel(value = "导入信息")
@Data
public class ImportInfoVo implements Serializable {

    private static final long serialVersionUID = 1L;

    // 序号
    @ApiModelProperty(value = "序号")
    @ExcelProperty("序号")
    private Integer index;

    // 姓名
    @ApiModelProperty(value = "姓名")
    @ExcelProperty(value = "姓名",converter = SpaceConverter.class)
    private String name;

    // 年龄
    @ApiModelProperty(value = "年龄")
    private Integer age;
  
  	// 手机号
    @ApiModelProperty(value = "手机号")
    @ExcelProperty(value = "手机号",converter = SpaceConverter.class)
    private String phone;

}

数据监听器

这里我做了修改,没有像官方那样写。因为官方那样写一个监听器处理一个导入时间,我这个改成一个通用的监听器,这个就适合数据不多的时候使用的监听器。数据太多会出现 oom

 /**
 * easyexcel 通用读取监听器
 * 此方法适合数据不大时使用 数据太大会出现 oom
 * @author liangdingji
 * @date 2024/3/22
 */
public class EasyExcelDataListener<T> implements ReadListener<T> {

    private static Logger logger = LoggerFactory.getLogger(EasyExcelDataListener.class);

    /**
     * 缓存的数据
     */
    private List<T> cachedDataList;

    public EasyExcelDataListener(List<T> cachedDataList){
        this.cachedDataList = cachedDataList;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(T data, AnalysisContext context) {
        logger.info("开始解析");
        cachedDataList.add(data);
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        logger.info("所有数据解析完成!");
    }
}

string类型转换器

这里官方原本是实现一些自定义类型转换的,比如 string 转date 之类的。这里我就实现了去空格的操作


/**
 * string类转换器 去除空格
 * @author liangdingji
 * @date 2024/3/25
 */
public class SpaceConverter implements Converter<String> {
    @Override
    public Class supportJavaTypeKey() {
        return String.class;
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    @Override
    public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        // 去除空格
        String originalValue = cellData.getStringValue();
        return StringUtils.isNotEmpty(originalValue) ? originalValue.replace(" ", "") : null;
    }

}

读取数据

@Test
    public void simpleRead() {
        String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
        List<EnterpriseImportInfoVo> voList = Lists.newArrayList();
        EasyExcel.read(fileName, ImportInfoVo.class, new EasyExcelDataListener<>(voList)).sheet().doRead();
    }

导入数据

序号 姓名 年龄 手机号 1 test 11 13800138000

坑来了

在读取手机号,不管怎么样都是为 null 的,有几个方法是可以获取值的

  • 去掉自定义转换器,这个去掉就不无法去除空格了,这个不能
  • 将Excel 的单元格的格式改成文本,这个也不能控制客户的操作呀,也不现实
  • 所以问题肯定是出在了转换器上

填坑

然后在 debug 的时候,就发现了端倪,看一下源码,就能看到CellData这玩意有好几种类型的值,这个我早就该想到的了,不同的数据肯定会有不同类型的数据类型去解析然后去接收,真的蠢……然后多加一个类型判断就可以了

public class CellData<T> extends AbstractCell {
    private CellDataTypeEnum type;
    private BigDecimal numberValue;
    private String stringValue;
    private Boolean booleanValue;
    private T data;
    private FormulaData formulaData;
}

正确的类型转换器


/**
 * string类转换器 去除空格
 * @author liangdingji
 * @date 2024/3/25
 */
public class SpaceConverter implements Converter<String> {
    @Override
    public Class supportJavaTypeKey() {
        return String.class;
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    @Override
    public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        String originalValue = "";
        if (cellData.getType() == CellDataTypeEnum.STRING){
             originalValue = cellData.getStringValue();
        }else if (cellData.getType() == CellDataTypeEnum.NUMBER){
            originalValue = cellData.getNumberValue().toBigInteger().toString();
        }
        return StringUtils.isNotEmpty(originalValue) ? originalValue.replace(" ", "") : null;
    }

}