EasyExcel实现导入并校验
easyexcel
快速、简洁、解决大文件内存溢出的java处理Excel工具
项目地址:https://gitcode.com/gh_mirrors/ea/easyexcel
免费下载资源
·
目录
一、需求描述
公司业务中需要用户导入Excel数据,并且每次几万条,要求速度快,用户操作方便灵活,有些非必填字段的数据可以为空。
当然目前有很多成熟的框架都可以实现该功能,在这里就不详细的描述我们是如何选择这些框架的过程,我们选择的是阿里的easyExcel框架,号称操作简单、省内存、性能稳定等。导入功能本身并没什么复杂的,这里主要想分享下我在导入Excel数据的过程中的校验操作(列头字段的校验、数据类型的校验),因为很多时候用户在导入的时候不依照模板进行,列头字段不正确、乱序,数据不该为空时候为空等等...
二、简单实现
1、引入pom依赖
<!--easyexcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.0.5</version>
</dependency>
2、创建监听器
@Slf4j
public class AtbDataListener extends AnalysisEventListener<AtbExcelVo> {
/**
* 批处理阈值
*/
private static final int BATCH_COUNT = 20;
@Getter
List<AtbExcelVo> list = new ArrayList<>(BATCH_COUNT);
@Override
public void invoke(AtbExcelVo atbExcelVo, AnalysisContext analysisContext) {
list.add(atbExcelVo);
if (list.size() >= BATCH_COUNT) {
list.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
3、创建实体类
@Data
@Setter
@Getter
@EqualsAndHashCode
public class AtbExcelVo implements Serializable {
private static final long serialVersionUID = 1L;
@ExcelProperty(value ="序号")
private String id;
@ExcelProperty(value = "订单号")
private String orderId;
@ExcelProperty(value = "约定交期")
private String deliveryTime;
@ExcelProperty(value = "客户编号")
private String cusCode;
@ExcelProperty(value = "客户简称")
private String shortName;
@ExcelProperty(value = "规格")
private String seriNum;
@ExcelProperty(value = "客户规格")
private String cusSeriNum;
@ExcelProperty(value = "产品编号")
private String prodCode;
@ExcelProperty(value = "产品名称")
private String ProdName;
@ExcelProperty(value = "数量")
private String prodCount;
@ExcelProperty("单位")
private String unit;
@ExcelProperty("单价")
private String unitPrice;
@ExcelProperty("订单日期")
private String orderTime;
@ExcelProperty("待完成工序")
private String unCompleteProcess;
@ExcelProperty("订单备注")
private String orderMark;
}
4、具体使用
public boolean importOrder(MultipartFile importFile) {
List<AtbExcelVo> list = EasyExcel.read(importFile.getInputStream(), AtbExcelVo.class, new AtbDataListener()).doReadAllSync();
}
小结:其实到目前为止导入功能基本可以实现了,只要将用户Excel的数据能够转化成特定list,后面基本都好操作了。但是这里仅仅是对用户的无误数据能够导入了,但是如果用户的数据存在很多问题,那么我们将如何来进行校验并让用户调整后继续导入?我能够想到的就是从实体上添加注解实现字段的校验。
三、加入校验功能
1、自定义注解
/**
* 导入Excel数据进行校验的注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelValid {
String message() default "导入有未填入的字段";
}
2、自定义校验
public class ExcelImportValid {
/**
* Excel导入字段校验
* @param object 校验的JavaBean 其属性须有自定义注解
*/
public static void valid(Object object) {
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
//设置可访问
field.setAccessible(true);
//属性的值
Object fieldValue = null;
try {
fieldValue = field.get(object);
} catch (IllegalAccessException e) {
throw new ContractException(ResponseStatusEnum.FAILED.getCode(),
field.getAnnotation(ExcelValid.class).message());
}
//是否包含必填校验注解
boolean isExcelValid = field.isAnnotationPresent(ExcelValid.class);
if (isExcelValid && Objects.isNull(fieldValue)) {
throw new ContractException(ResponseStatusEnum.FAILED.getCode(),field.getAnnotation(ExcelValid.class).message());
}
}
}
}
3、调整后的实体类
@Data
@Setter
@Getter
@EqualsAndHashCode
public class AtbExcelVo implements Serializable {
private static final long serialVersionUID = 1L;
@ExcelProperty(value ="序号")
@ExcelValid(message = "【序号】列不能有空数据!")
private String id;
@ExcelProperty(value = "订单号")
private String orderId;
@ExcelProperty(value = "约定交期")
@ExcelValid(message = "【约定交期】列不能有空数据!")
private String deliveryTime;
@ExcelProperty(value = "客户编号")
private String cusCode;
@ExcelProperty(value = "客户简称")
private String shortName;
@ExcelProperty(value = "规格")
@ExcelValid(message = "【规格】列不能有空数据!")
private String seriNum;
@ExcelProperty(value = "客户规格")
private String cusSeriNum;
@ExcelProperty(value = "产品编号")
@ExcelValid(message = "【产品编号】列不能有空数据!")
private String prodCode;
@ExcelProperty(value = "产品名称")
private String ProdName;
@ExcelProperty(value = "数量")
@ExcelValid(message = "【数量】列不能有空数据!")
private String prodCount;
@ExcelProperty("单位")
private String unit;
@ExcelProperty("单价")
private String unitPrice;
@ExcelProperty("订单日期")
@ExcelValid(message = "【订单日期】列不能有空数据!")
private String orderTime;
@ExcelProperty("待完成工序")
private String unCompleteProcess;
@ExcelProperty("订单备注")
private String orderMark;
}
4、监听器
@Slf4j
public class AtbListener extends AnalysisEventListener<AtbExcelVo> {
/**
* 批处理阈值
*/
private static final int BATCH_COUNT = 20;
@Getter
List<AtbExcelVo> list = new ArrayList<>(BATCH_COUNT);
@Override
public void invoke(AtbExcelVo atbExcelVo, AnalysisContext analysisContext) {
log.info("数据校验:"+atbExcelVo);
ExcelImportValid.valid(atbExcelVo);
list.add(atbExcelVo);
if (list.size() >= BATCH_COUNT) {
log.info("已经解析"+list.size()+"条数据");
list.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
小结:到这里为止目前能对字段下的数据进行校验了,但是还是不能实现对列头字段的校验,这里需要在监听器上进行调整,具体的调整如下:
5、最终监听器:
@Slf4j
public class AtbListener extends AnalysisEventListener<AtbExcelVo> {
/**
* 批处理阈值
*/
private static final int BATCH_COUNT = 20;
@Getter
List<AtbExcelVo> list = new ArrayList<>(BATCH_COUNT);
@Override
public void invoke(AtbExcelVo atbExcelVo, AnalysisContext analysisContext) {
log.info("数据校验:"+atbExcelVo);
ExcelImportValid.valid(atbExcelVo);
list.add(atbExcelVo);
if (list.size() >= BATCH_COUNT) {
log.info("已经解析"+list.size()+"条数据");
list.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
log.info("解析到一条表头数据:{}", JSON.toJSONString(headMap));
List<String> voHead = new ArrayList<>();
Field[] fields = AtbExcelVo.class.getDeclaredFields();
for (int i = 1; i < fields.length; i++) {
ExcelProperty myField2 = fields[i].getAnnotation(ExcelProperty.class);
voHead.add(myField2.value()[0]);
}
int i=0;
for(Integer key : headMap.keySet()){
log.info("表头数据校验:"+headMap.get(key));
if(headMap.get(key) == null ){
throw new ContractException("1","第"+(i+1)+"列表头格式不正确!");
}
if(i == voHead.size()){
throw new ContractException("1","上传文件表头超过模板列数,正确表头为:"+voHead);
}
if(!headMap.get(key).equals(voHead.get(i))){
throw new ContractException("1", "第"+(i+1)+"列【"+headMap.get(key)+"】附近表头模板异常!\n正确表头顺序为:\n"+voHead+ "!");
}
i++;
}
}
}
四、总结
到此导入功能就实现了,本文中用到的自定义异常ContractException没有分享,这里如果实现没有自定义异常的直接抛出Exception也行。导入的关键点还是在于监听器的使用,不管是列头的校验还是数据的校验都离不开监听器。
官方文档:读Excel | Easy Excel
GitHub 加速计划 / ea / easyexcel
31.64 K
7.47 K
下载
快速、简洁、解决大文件内存溢出的java处理Excel工具
最近提交(Master分支:3 个月前 )
c42183df
Bugfix 3 个月前
efa7dff6 * 重新加回 `commons-io`
3 个月前
更多推荐
已为社区贡献4条内容
所有评论(0)