目录

1.下载指定的excel数据模板
2.excel模板写入数据并导入
3.导入的时候根据校验规则进行筛选,导入成功的返回成功列表,数据存在问题的返回失败列表,失败列表支持数据编辑修正
看到需求的第一眼,可能就觉得第三点有点难度,我们知道,传统的数据校验可以通过在传输对象dto上面加注解实现。
- //第一种
- public Result test1(@RequestBody @Validated TestDTO dto) {...}
-
- //第二种
- public Result test2(@RequestBody @Valid TestDTO dto{...}
- //第三种
- public Result test3(@RequestBody @Validated(value = {SaveGroup.class}) TestDTO dto) {...}
-
TestDTO里面会有一些类似 @NotNull、@NotBlank、@Size等校验注解,这里就不列了。
然后在全局异常拦截那里进行统一封装,使其返回的数据结构尽量保持统一,所以一般还得有一个RestExceptionHandler类。
- @ControllerAdvice
- public class RestExceptionHandler {
-
- /**
- * 处理参数验证失败异常
- * @param e
- * @return
- */
- @ExceptionHandler(MethodArgumentNotValidException.class)
- @ResponseBody
- @ResponseStatus(HttpStatus.OK)
- private Response> methodArgumentNotValidException(MethodArgumentNotValidException e) {
- log.warn("MethodArgumentNotValidException", e);
- FieldError fieldError = e.getBindingResult().getFieldError();
- return ResponseUtils.create(CommonCodeEnum.VALIDATE_ERROR.getCode(), CommonCodeEnum.VALIDATE_ERROR.getMessage(), fieldError.getDefaultMessage());
- }
- }
-
讲到常见的数据校验,那么我们画风一转,再回来看需求,可见以上是不满足需求的,首先,我们的入参是一个文件流(指定的Excel模板文件),我们得先解析文件再进行数据校验,合法的放一个集合,不合法的放另一个集合;再者,即使入参是一个数组,这种校验一旦不满足立马进异常处理了,无法返回给前端正确的数据结构,所以今天就分享解决这类需求的解决方案。
基础数据
UserExcelVO
- import lombok.Data;
-
- import java.util.List;
-
- /**
- *
- */
- @Data
- public class UserExcelVO {
-
- /**
- * 成功列表
- */
- private List
success; -
- /**
- * 失败列表
- */
- private List
fail; - }
UserExcel
- import com.alibaba.excel.annotation.ExcelProperty;
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
-
- import javax.validation.constraints.NotBlank;
- import javax.validation.constraints.Pattern;
- import javax.validation.constraints.Size;
- import java.io.Serializable;
-
- /**
- *
- */
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class UserExcel implements Serializable {
-
- @NotBlank(message = "手机号不能为空")
- @Size(max = 4)
- @ExcelProperty(value = "用户名", index = 0)
- private String name;
-
- @ExcelProperty(value = "年龄", index = 1)
- private Integer age;
-
- @Pattern(regexp = "^[1][3,4,5,7,8][0-9]{9}$$", message = "手机号不合法")
- @NotBlank(message = "手机号不能为空")
- @ExcelProperty(value = "手机号", index = 2)
- private String mobile;
-
- @ExcelProperty(value = "性别", index = 3)
- private String sex;
-
- }
excel模板数据:

- import com.alibaba.excel.EasyExcel;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.web.bind.annotation.PostMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.RestController;
- import org.springframework.web.multipart.MultipartFile;
-
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.List;
-
- /**
- *
- */
- @RestController
- @RequestMapping("/excel")
- public class ExcelController {
-
- @PostMapping("/importExcel1")
- public UserExcelVO importExcel(@RequestParam("file") MultipartFile file) {
- List
list; - List
fail = new ArrayList<>(); - UserExcelVO userExcelVO = new UserExcelVO();
- String mobileReg = "^[1][3,4,5,7,8][0-9]{9}$";
- try {
- list = EasyExcel.read(file.getInputStream(), UserExcel.class, new ModelExcelListener()).sheet().doReadSync();
-
- list.forEach(data -> {
- // 处理姓名的校验
- if (StringUtils.isEmpty(data.getName()) || data.getName().length() > 4) {
- fail.add(data);
- return;
- }
- // 处理手机号的校验
- if (StringUtils.isEmpty(data.getMobile()) || !data.getMobile().matches(mobileReg)) {
- fail.add(data);
- return;
- }
- // 以下根据字段多少可能有n个if...
-
- });
- userExcelVO.setFail(fail);
- list.removeAll(fail);
- userExcelVO.setSuccess(list);
- } catch (IOException e) {
- e.printStackTrace();
- }
- return userExcelVO;
- }
-
- }
实际的业务场景,一个excel里面假如是订单数据,最少是几十个字段起步的,难道要写几十个if else吗?方案一明显是不合理的,因此使用注解的方式帮我们解决。
ValidationUtils
- import javax.validation.Validation;
- import javax.validation.Validator;
- import javax.validation.ValidatorFactory;
-
- /**
- *
- */
- public class ValidationUtils {
-
- public static Validator getValidator() {
- return validator;
- }
-
- static Validator validator;
-
- static {
- ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
- validator = validatorFactory.getValidator();
- }
-
- }
ModelExcelListener
- import com.alibaba.excel.context.AnalysisContext;
- import com.alibaba.excel.event.AnalysisEventListener;
- import lombok.extern.slf4j.Slf4j;
-
- import java.util.ArrayList;
- import java.util.List;
-
- /**
- *
- */
- @Slf4j
- public class ModelExcelListener extends AnalysisEventListener
{ -
- private List
datas = new ArrayList<>(); -
- /**
- * 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据
- */
- @Override
- public void invoke(UserExcel data, AnalysisContext context) {
- //数据存储到list,供批量处理,或后续自己业务逻辑处理。
- log.info("读取到数据{}",data);
- datas.add(data);
- //根据业务自行处理,可以写入数据库等等
-
- }
-
- //所有的数据解析完了调用
- @Override
- public void doAfterAllAnalysed(AnalysisContext context) {
- log.info("所有数据解析完成");
- }
-
- }
请求:
- import com.alibaba.excel.EasyExcel;
- import org.springframework.web.bind.annotation.PostMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.RestController;
- import org.springframework.web.multipart.MultipartFile;
-
- import javax.validation.ConstraintViolation;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Set;
-
- /**
- *
- */
- @RestController
- @RequestMapping("/excel")
- public class ExcelController {
-
- @PostMapping("/importExcel2")
- public UserExcelVO importExcelV2(@RequestParam("file") MultipartFile file) {
- List
list; - List
fail = new ArrayList<>(); - UserExcelVO userExcelVO = new UserExcelVO();
- try {
- list = EasyExcel.read(file.getInputStream(), UserExcel.class, new ModelExcelListener()).sheet().doReadSync();
- list.forEach(data -> {
- Set
> violations = ValidationUtils.getValidator().validate(data); - if (violations.size() > 0) {
- fail.add(data);
- }
- });
- userExcelVO.setFail(fail);
- list.removeAll(fail);
- userExcelVO.setSuccess(list);
- } catch (IOException e) {
- e.printStackTrace();
- }
- return userExcelVO;
- }
-
- }
方案一的结果:
- {
- "success": [
- {
- "name": "张2",
- "age": 19,
- "mobile": "13056781235",
- "sex": "女"
- },
- {
- "name": "张3",
- "age": 20,
- "mobile": "13056781236",
- "sex": "男"
- },
- {
- "name": "张4",
- "age": 21,
- "mobile": "13056781237",
- "sex": "女"
- },
- {
- "name": "张5",
- "age": 22,
- "mobile": "13056781238",
- "sex": "男"
- },
- {
- "name": "张6",
- "age": 23,
- "mobile": "13056781239",
- "sex": "男"
- },
- {
- "name": "张7",
- "age": 24,
- "mobile": "13056781240",
- "sex": "男"
- },
- {
- "name": "张8",
- "age": 25,
- "mobile": "13056781241",
- "sex": "男"
- },
- {
- "name": "张9",
- "age": 26,
- "mobile": "13056781242",
- "sex": "男"
- }
- ],
- "fail": [
- {
- "name": "张1",
- "age": 18,
- "mobile": "3056781234",
- "sex": "男"
- },
- {
- "name": "张10",
- "age": 27,
- "mobile": "130567812436",
- "sex": "男"
- }
- ]
- }
方案二的结果:
- {
- "success": [
- {
- "name": "张2",
- "age": 19,
- "mobile": "13056781235",
- "sex": "女"
- },
- {
- "name": "张3",
- "age": 20,
- "mobile": "13056781236",
- "sex": "男"
- },
- {
- "name": "张4",
- "age": 21,
- "mobile": "13056781237",
- "sex": "女"
- },
- {
- "name": "张5",
- "age": 22,
- "mobile": "13056781238",
- "sex": "男"
- },
- {
- "name": "张6",
- "age": 23,
- "mobile": "13056781239",
- "sex": "男"
- },
- {
- "name": "张7",
- "age": 24,
- "mobile": "13056781240",
- "sex": "男"
- },
- {
- "name": "张8",
- "age": 25,
- "mobile": "13056781241",
- "sex": "男"
- },
- {
- "name": "张9",
- "age": 26,
- "mobile": "13056781242",
- "sex": "男"
- }
- ],
- "fail": [
- {
- "name": "张1",
- "age": 18,
- "mobile": "3056781234",
- "sex": "男"
- },
- {
- "name": "张10",
- "age": 27,
- "mobile": "130567812436",
- "sex": "男"
- }
- ]
- }
发现两种方案的测试结果虽然是一样的,但是很明显,方案二更优秀。我们后续写代码的时候,除了做功能,也要考虑代码的扩展性,不然产品说加个功能,我们又得吭哧吭哧写代码了。
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、评论、收藏➕关注,您的支持是我坚持写作最大的动力。