依赖版本
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.1</version>
</dependency>
监听器
@Slf4j
public class UploadDataListener<T> implements ReadListener<T> {
/** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 */
private static final int BATCH_COUNT = 100;
/** 缓存的数据 */
private List<T> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/** Predicate用于过滤数据 */
private Predicate<T> predicate;
/** 调用持久层批量保存 */
private Consumer<Collection<T>> consumer;
public UploadDataListener(Predicate<T> predicate, Consumer<Collection<T>> consumer) {
this.predicate = predicate;
this.consumer = consumer;
}
public UploadDataListener(Consumer<Collection<T>> consumer) {
this.consumer = consumer;
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/
/**
* 这个每一条数据解析都会来调用
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(T data, AnalysisContext context) {
if (predicate != null && !predicate.test(data)) {
return;
}
cachedDataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (cachedDataList.size() >= BATCH_COUNT) {
try {
// 执行具体消费逻辑
consumer.accept(cachedDataList);
} catch (Exception e) {
log.error("Failed to upload data!data={}", cachedDataList);
throw new BizException("导入失败");
}
// 存储完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
if (CollUtil.isNotEmpty(cachedDataList)) {
try {
// 执行具体消费逻辑
consumer.accept(cachedDataList);
log.info("所有数据解析完成!");
} catch (Exception e) {
log.error("Failed to upload data!data={}", cachedDataList);
// 抛出自定义的提示信息
if (e instanceof BizException) {
throw e;
}
throw new BizException("导入失败");
}
}
}
}
调用
try {
EasyExcel.read(
file.getInputStream(),
HealthClassImportDTO.class,
new UploadDataListener<HealthClassImportDTO>(
list -> {
// 校验数据
ValidationUtils.validate(list);
// dao 保存···
log.info("从Excel导入数据一共 {} 行 ", list.size());
}))
.sheet()
.doRead();
} catch (IOException e) {
log.error("导入失败", e);
throw new BizException("导入失败");
}
转换器
public class LocalDateConverter implements Converter<LocalDate> {
@Override
public Class<LocalDate> supportJavaTypeKey() {
return LocalDate.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public LocalDate convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
if(null==cellData) {
return null;
}
LocalDate result=null;
if(cellData.getType()==CellDataTypeEnum.NUMBER) {
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
Date date= DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(),
globalConfiguration.getUse1904windowing(), null);
result =date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
} else {
Date date= DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(),
contentProperty.getDateTimeFormatProperty().getUse1904windowing(), null);
result =date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
}if(cellData.getType()==CellDataTypeEnum.STRING) {
String value=cellData.getStringValue();
if(value.contains("-")) {
try {
result= LocalDate.parse(cellData.getStringValue());
} catch (Exception e) {
e.printStackTrace();
}
}
else if(value.contains("/")) {
try {
result= LocalDate.parse(new SimpleDateFormat("yyyy/MM/dd").format( value) );
} catch (Exception e) {
e.printStackTrace();
}
}
}
return result;
}
@Override
public WriteCellData<?> convertToExcelData(LocalDate value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return new WriteCellData<>(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
}
}
兼容时间格式(yyyy/MM/dd、yyyy-MM-ddd)
@Data
public class HealthClassImportDTO implements Serializable {
private static final long serialVersionUID = -1753550934555684092L;
@NotNull(message = "显示日期不能为空")
@ExcelProperty(value = "显示日期", converter = LocalDateConverter.class, index = 1)
private LocalDate viewDate;
}