在使用easyexcel
进行导入时,遇到各种问题,下面进行总结归纳。
导入阿里巴巴EasyExcel
的maven
依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
新建POJO
类,然后找到需要转换的字段,在类上添加EasyExcel
的注解@ExcelProperty
,value
对应excel
的列名,index
对应excel
的列号(从0
开始)。
@AllArgsConstructor
@NoArgsConstructor //必须要保证无参构造方法存在,否则会报初始化对象失败
@Data
public class InspectionTablePOJO {
@ExcelProperty(value = "表名称", index = 0)
@NotNull
private String inspectionTableName;
@ExcelProperty(value = "设备类型编号", index = 1)
@NotNull
private String equipmentTypeId;
@ExcelProperty(value = "表建立时间", index = 2,converter = InstantConverter.class)
@NotNull
// @LocalDateTimeFormat} 注解 ,作用: 与 // {@code @ExcelProperty(converter = LocalDateTimeExcelConverter.class)} 搭配使用,
// 指定导入导出的时间格式.
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Instant tableCreationTime;
}
这里如果直接导入,由于excel
的日期是字符串或者其他格式,不能转化为Instant
类,会报异常,参考文章,需要指定Converter
, 即自定义转换器,因为我们要转换Instant
,所以编写如下转换器!注意要实现的是com.alibaba.excel.converters.Converter
接口,定义完后在ExcelProperty
中指定converter
。
InstantConverter
代码如下:
public class InstantConverter implements Converter<Instant> {
@Override
public Class<Instant> supportJavaTypeKey() {
return Instant.class;
}
// excel中日期指定为文本格式,因此指定为STRING,如果为其他格式则需要指定为其他格式
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public Instant convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws ParseException {
// 将字符串转换为Instant
Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(cellData.getStringValue());
return date.toInstant();
}
@Override
public CellData<String> convertToExcelData(Instant value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
// 将Instant转换为字符串
String out = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(value);
return new CellData(out);
}
}
写入excel sheet
中时,转换为指定格式(value
)的字符串;
从excel sheet
中读取value
格式的字符串,转换为对应的数字类型
在Service
层代码如下:
import static com.fii.amms.exception.ErrorEnum.FAILED_TO_ADD;
@Service
@Slf4j
public class BatchImportService{
@Autowired
InspectionTableMapper inspectionTableMapper;
@Autowired
InspectionTableConverter inspectionTableConverter;
@Autowired
InspectionTableExcelConverter inspectionTableExcelConverter;
public int importInspectionTableExcel(MultipartFile file) throws AMMSException{
ExcelUtil excelUtil = new ExcelUtil();
try{
excelUtil.simpleImportInspectionTable(file, inspectionTableMapper, inspectionTableConverter, inspectionTableExcelConverter);
}catch(Exception e){
e.printStackTrace();
throw new AMMSException(FAILED_TO_ADD.getCode(),"导入表失败",FAILED_TO_ADD.getMsg());表名称", index = 0)
@NotNull
}
return 1;
}
}
public class ExcelUtil {
public void simpleImportInspectionTable(MultipartFile file, InspectionTableMapper inspectionTableMapper, InspectionTableConverter inspectionTableConverter, InspectionTableExcelConverter inspectionTableExcelConverter){
String fileName = file.getOriginalFilename();
File dest = new File(new File("/").getAbsolutePath()+"/home/2020/"+fileName);
System.out.println(dest.toString());
if(!dest.getParentFile().exists()){
dest.getParentFile().mkdirs();
}
try{
file.transferTo(dest);
} catch (IOException e) {
e.printStackTrace();
} catch(IllegalStateException ie){
ie.printStackTrace();
}
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(dest, InspectionTablePOJO.class, new InspectionTableExcelListener(inspectionTableMapper, inspectionTableConverter, inspectionTableExcelConverter)).sheet().doRead();
}
}
编写阅读监听类,Excel
文件的读操作,是通过此监听类实现的,EasyExcel
是一行一行的读取文件,所以要把每一次读取的数据保存到数据库中,为了避免重复,保存之前会判断是否存在相同的数据。
// 由于在实际中可能会根据不同的业务场景需要的读取到的不同的excel表的数据进行不同操作,
// 所以这里将ExcelListener作为所有listener的基类,根据读取不同的java模型自定义一个listener类继承ExcelListener,
// 根据不同的业务场景选择性对以下方法进行重写,具体如com.luwei.listener.OrderListener所示
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Component
public class InspectionTableExcelListener extends AnalysisEventListener<InspectionTablePOJO> {
private static final Logger LOGGER = LoggerFactory.getLogger(InspectionTableExcelListener.class);
private static final int BATCH_COUNT = 5;
// 自定义用于暂时存储data。
// 可以通过实例获取该值
// 可以指定AnalysisEventListener的泛型来确定List的存储类型
List<InspectionTablePOJO> list = new ArrayList<>();
private InspectionTableMapper inspectionTableMapper;
private InspectionTableConverter inspectionTableConverter;
private InspectionTableExcelConverter inspectionTableExcelConverter;
public InspectionTableExcelListener(InspectionTableMapper inspectionTableMapper, InspectionTableConverter inspectionTableConverter, InspectionTableExcelConverter inspectionTableExcelConverter){
this.inspectionTableMapper = inspectionTableMapper;
this.inspectionTableConverter = inspectionTableConverter;
this.inspectionTableExcelConverter = inspectionTableExcelConverter;
}
// 每隔N条存执行一次saveData()方法,
// 如果是入库操作,可使用默认的3000条,然后清理list,方便内存回收
// 每解析一行会回调invoke()方法。
// 如果当前行无数据,该方法不会执行,
// 也就是说如果导入的的excel表无数据,该方法不会执行,
// 不需要对上传的Excel表进行数据非空判断
// @param object 当前读取到的行数据对应的java模型对象
// @param context 定义了获取读取excel相关属性的方法
@Override
public void invoke(InspectionTablePOJO inspectionTablePOJO, AnalysisContext analysisContext){
System.out.println(inspectionTablePOJO.getInspectionTableName());
System.out.println(inspectionTablePOJO.toString());
list.add(inspectionTablePOJO);
if(list.size()>=BATCH_COUNT){
saveData();
list.clear();
}
}
// 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则继续读取下一行。
// 如果不重写该方法,默认抛出异常,停止读取
@Override
public void onException(Exception exception, AnalysisContext context) {
LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
// 如果是某一个单元格的转换异常 能获取到具体行号
// 如果要获取头的信息 配合invokeHeadMap使用
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex());
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext){
saveData();
LOGGER.info("所有数据解析完成!");
}
JAVA简单快速的读写Excel之EasyExcel
private void saveData(){
for(InspectionTablePOJO inspectionTablePOJO:list){
try{
InspectionTableDTO inspectionTableDTO = inspectionTableExcelConverter.toDTO(inspectionTablePOJO);
InspectionTableDO inspectionTableDO = inspectionTableConverter.toDO(inspectionTableDTO);
this.inspectionTableMapper.addInspectionTable(inspectionTableDO);
}catch(Exception e){
e.printStackTrace();
}
LOGGER.info("{}条数据,开始存储数据库!",list.size());
LOGGER.info("存储数据库成功!");
}
}
}
参考文献:
Java EasyExcel读取Excel数据转换等异常处理示例代码
POI和EasyExcel-你还在为导入导出数据苦恼吗?
简单的Excel导入(上传、解析、持久化)
java日期互转:LocalDateTime、String、TimeStamp、Long、Instant、Date
使用EasyExcel上传Excel文件,并把数据保存到数据库中
为easyexcel设置TimeZone
重点easyexcel-utils
将字符串转换为Instant
将Instant格式化为String
EasyExcel自定义Converter解决LocalDateTime日期转换的问题
EasyExcel读取Excel日期为数字如何解决
重点还没看EasyExcel实现Excel文件的导入导出
EASYEXCEL自定义CONVERTER解决性别转换问题
JAVA简单快速的读写Excel之EasyExcel
EasyExcel 轻松灵活读取Excel内容
EasyExcel ----- 实现数据库数据 导出为Excel表和Excel表导入数据功能
MultipartFile 类