使用Java實現(xiàn)Excel導入并進行數(shù)據(jù)校驗
一、產(chǎn)品需求
1.下載指定的excel數(shù)據(jù)模板
2.excel模板寫入數(shù)據(jù)并導入
3.導入的時候根據(jù)校驗規(guī)則進行篩選,導入成功的返回成功列表,數(shù)據(jù)存在問題的返回失敗列表,失敗列表支持數(shù)據(jù)編輯修正
看到需求的第一眼,可能就覺得第三點有點難度,我們知道,傳統(tǒng)的數(shù)據(jù)校驗可以通過在傳輸對象dto上面加注解實現(xiàn)。

//第一種
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等校驗注解,這里就不列了。
然后在全局異常攔截那里進行統(tǒng)一封裝,使其返回的數(shù)據(jù)結(jié)構(gòu)盡量保持統(tǒng)一,所以一般還得有一個RestExceptionHandler類。
@ControllerAdvice
public class RestExceptionHandler {
/**
* 處理參數(shù)驗證失敗異常
* @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());
}
}講到常見的數(shù)據(jù)校驗,那么我們畫風一轉(zhuǎn),再回來看需求,可見以上是不滿足需求的,首先,我們的入?yún)⑹且粋€文件流(指定的Excel模板文件),我們得先解析文件再進行數(shù)據(jù)校驗,合法的放一個集合,不合法的放另一個集合;再者,即使入?yún)⑹且粋€數(shù)組,這種校驗一旦不滿足立馬進異常處理了,無法返回給前端正確的數(shù)據(jù)結(jié)構(gòu),所以今天就分享解決這類需求的解決方案。
二、解決方法
基礎數(shù)據(jù)
UserExcelVO
import lombok.Data;
import java.util.List;
/**
*
*/
@Data
public class UserExcelVO {
/**
* 成功列表
*/
private List<UserExcel> success;
/**
* 失敗列表
*/
private List<UserExcel> 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模板數(shù)據(jù):

方案一:大量if-else判斷校驗
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<UserExcel> list;
List<UserExcel> 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;
}
// 以下根據(jù)字段多少可能有n個if...
});
userExcelVO.setFail(fail);
list.removeAll(fail);
userExcelVO.setSuccess(list);
} catch (IOException e) {
e.printStackTrace();
}
return userExcelVO;
}
}方案二:請求體加入注解進行校驗
實際的業(yè)務場景,一個excel里面假如是訂單數(shù)據(jù),最少是幾十個字段起步的,難道要寫幾十個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<UserExcel> {
private List<UserExcel> datas = new ArrayList<>();
/**
* 通過 AnalysisContext 對象還可以獲取當前 sheet,當前行等數(shù)據(jù)
*/
@Override
public void invoke(UserExcel data, AnalysisContext context) {
//數(shù)據(jù)存儲到list,供批量處理,或后續(xù)自己業(yè)務邏輯處理。
log.info("讀取到數(shù)據(jù){}",data);
datas.add(data);
//根據(jù)業(yè)務自行處理,可以寫入數(shù)據(jù)庫等等
}
//所有的數(shù)據(jù)解析完了調(diào)用
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.info("所有數(shù)據(jù)解析完成");
}
}請求:
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<UserExcel> list;
List<UserExcel> fail = new ArrayList<>();
UserExcelVO userExcelVO = new UserExcelVO();
try {
list = EasyExcel.read(file.getInputStream(), UserExcel.class, new ModelExcelListener()).sheet().doReadSync();
list.forEach(data -> {
Set<ConstraintViolation<UserExcel>> 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;
}
}三、測試結(jié)果
方案一的結(jié)果:
{
"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": "男"
}
]
}方案二的結(jié)果:
{
"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": "男"
}
]
}發(fā)現(xiàn)兩種方案的測試結(jié)果雖然是一樣的,但是很明顯,方案二更優(yōu)秀。我們后續(xù)寫代碼的時候,除了做功能,也要考慮代碼的擴展性,不然產(chǎn)品說加個功能,我們又得吭哧吭哧寫代碼了。
以上就是使用Java實現(xiàn)Excel導入并進行數(shù)據(jù)校驗的詳細內(nèi)容,更多關(guān)于Java Excel導入與數(shù)據(jù)校驗的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java使用動態(tài)代理來實現(xiàn)AOP(日志記錄)的實例代碼
AOP(面向方面)的思想,就是把項目共同的那部分功能分離開來,比如日志記錄,避免在業(yè)務邏輯里面夾雜著跟業(yè)務邏輯無關(guān)的代碼2013-09-09
Java Spring Security認證與授權(quán)及注銷和權(quán)限控制篇綜合解析
Spring Security 是 Spring 家族中的一個安全管理框架,實際上,在 Spring Boot 出現(xiàn)之前,Spring Security 就已經(jīng)發(fā)展了多年了,但是使用的并不多,安全管理這個領(lǐng)域,一直是 Shiro 的天下2021-10-10
關(guān)于@SpringBootApplication詳解
這篇文章主要介紹了關(guān)于@SpringBootApplication的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08
@Autowired注解注入的xxxMapper報錯問題及解決
這篇文章主要介紹了@Autowired注解注入的xxxMapper報錯問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
詳解Spring Security如何在權(quán)限中使用通配符
小伙伴們知道,在Shiro中,默認是支持權(quán)限通配符的?,F(xiàn)在給用戶授權(quán)的時候,可以一個權(quán)限一個權(quán)限的配置,也可以直接用通配符。本文將介紹Spring Security如何在權(quán)限中使用通配符,需要的可以參考一下2022-06-06

