excel导入,使用注解对字段进行逻辑判断(字段是否为空,数据结构等)条件
1.创建一个工具类
package com.yj.utils;
import cn.hutool.core.io.FileUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.yj.annotation.ImportFieldCheckAnnotation;
import com.yj.exception.ServiceException;
import com.yj.service.SysDictDetailService;
import com.yj.utils.spring.SpringUtils;
import com.yj.vo.SysDictDetailVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
import static com.yj.constant.ErrorCodeConstants.*;
import static com.yj.utils.ServiceExceptionUtil.exception;
/**
* @Author: hjc
* @Date: 2024/5/25 18:59
* @Description:
**/
@Slf4j
@Service
public class ExcelUtils<T> {
/**
* 校验表头是否正确
*
* @param wb
* @param columnName
* @return
* @throws Exception
*/
public static void verificationExcelHeadLine(int rowNum, Workbook wb, String[] columnName) {
try {
Sheet sheet = wb.getSheetAt(0);
Row row = sheet.getRow(rowNum);
if (row != null && row.getLastCellNum() == columnName.length) {
int lastCellNum = row.getLastCellNum();
for (int idx = 0; idx <lastCellNum ; idx++) {
String value = row.getCell(idx).getStringCellValue();
if (StringUtils.isBlank(value) || !value.trim().startsWith(columnName[idx])) {
throw exception(FILE_HEAD_ERROR);
}
}
}else{
throw exception(FILE_HEAD_ERROR);
}
} catch (ServiceException e){
throw e;
} catch (Exception ex) {
log.info("表头验证失败=={}",ex);
throw exception(FILE_HEAD_ERROR);
}
}
/**
* 保存失败文件
* @param errorInfoList
* @param aclass
* @return
*/
public String saveFailResultFile(List<T> errorInfoList,Class aclass) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
String failResultFileName = "failresult" + UUID.randomUUID().toString().replace("-", "") + ".xlsx";
ExcelWriter excelWriter = EasyExcel.write(os, aclass)
.build();
WriteSheet sheet = EasyExcel.writerSheet(0, "Sheet1").build();
excelWriter.write(errorInfoList, sheet);
excelWriter.finish();
InputStream is = new ByteArrayInputStream(os.toByteArray());
String tempPath=System.getProperty("user.dir")+File.separator+"temp"+ File.separator;
File dir = new File(tempPath);
if (!dir.exists()) {
dir.mkdirs();
}
FileUtil.writeFromStream(is, tempPath + failResultFileName);
return failResultFileName;
}
/**
* 文件类型校验
* @param file
* @return
*/
@NotNull
public static String fileTypeCheck(MultipartFile file) {
if (file == null) {
log.error("上传文件为空");
throw exception(FILE_NOT_NULL);
}
//获取原始文件名
String fileName = file.getOriginalFilename();
//文件后缀名校验
if (!(fileName.endsWith("xls") || fileName.endsWith("xlsx"))) {
log.error("文件上传格式错误");
exception(FILE_TYPE_ERROR);
}
return fileName;
}
/**
* 设置响应结果
*
* @param response 响应结果对象
* @param rawFileName 文件名
* @throws UnsupportedEncodingException 不支持编码异常
*/
public static void setExcelResponseProp(HttpServletResponse response, String rawFileName) throws UnsupportedEncodingException {
//设置内容类型
response.setContentType("application/vnd.vnd.ms-excel");
//设置编码格式
response.setCharacterEncoding("utf-8");
//设置导出文件名称(避免乱码)
String fileName = URLEncoder.encode(rawFileName.concat(".xlsx"), "UTF-8");
// 设置响应头
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
}
/**
* 字段校验
*
* @param fields
* @param t
*/
public List<String> importCheck(Field[] fields, T t, List<String> errorTips) {
try {
for (Field field : fields) {
field.setAccessible(true);
Object obj = field.get(t);
ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
ImportFieldCheckAnnotation classFieldTypeAnnotation = field.getAnnotation(ImportFieldCheckAnnotation.class);
String[] chineseName = annotation.value();
if (ObjectUtils.isNull(obj) &&!ObjectUtils.isNull(classFieldTypeAnnotation)&&!classFieldTypeAnnotation.permitNull()) {
errorTips.add(chineseName[0] + "不能为空");
continue;
}
if(!ObjectUtils.isNull(obj)&&!ObjectUtils.isNull(classFieldTypeAnnotation)&&classFieldTypeAnnotation.isNumber()){
if (!this.isNumeric((String) obj)) {
errorTips.add(chineseName[0]+"字段为整型");
}else{
String str= (String) obj;
int min = classFieldTypeAnnotation.min();
int max = classFieldTypeAnnotation.max();
if(str.length()>8||Integer.valueOf(str)>max||Integer.valueOf(str)<min){
errorTips.add(chineseName[0] + "数据范围"+min+"~"+max);
}
}
}
if(!ObjectUtils.isNull(obj)&&!ObjectUtils.isNull(classFieldTypeAnnotation)&&classFieldTypeAnnotation.isDouble()){
if (!this.doubleCheck((String) obj)) {
if(StringUtils.isNotBlank(classFieldTypeAnnotation.msg())){
errorTips.add(classFieldTypeAnnotation.msg());
}else {
errorTips.add(chineseName[0] + "字段格式错误");
}
}else{
String str= (String) obj;
int min = classFieldTypeAnnotation.min();
int max = classFieldTypeAnnotation.max();
if(str.length()>12||Double.valueOf(str)>max||Double.valueOf(str)<min){
errorTips.add(chineseName[0] + "数据范围"+min+"~"+max);
}
}
}
if(!ObjectUtils.isNull(classFieldTypeAnnotation)&&!ObjectUtils.isNull(obj)){
boolean isTtr = obj instanceof String;
if(isTtr) {
String str = (String) obj;
if (classFieldTypeAnnotation.length()!=0&&str.length() > classFieldTypeAnnotation.length()) {
if(StringUtils.isNotBlank(classFieldTypeAnnotation.msg())){
errorTips.add(classFieldTypeAnnotation.msg());
}else {
errorTips.add(chineseName[0] + "字符长度过长");
}
}
}
}
//校验下拉选的值
if(!ObjectUtils.isNull(classFieldTypeAnnotation)&&!ObjectUtils.isNull(obj)&&StringUtils.isNotBlank(classFieldTypeAnnotation.dictCode())){
SysDictDetailService sysDictDetailService = SpringUtils.getBean(SysDictDetailService.class);
SysDictDetailVO sysDictDetailVO = new SysDictDetailVO();
sysDictDetailVO.setDictCode(classFieldTypeAnnotation.dictCode());
sysDictDetailVO.setDictLabel((String) obj);
List<SysDictDetailVO> list = sysDictDetailService.list(sysDictDetailVO);
if(CollectionUtils.isEmpty(list)){
errorTips.add(chineseName[0] + "不匹配");
}
}
//校验正则
if(!ObjectUtils.isNull(classFieldTypeAnnotation)&&!ObjectUtils.isNull(obj)&&StringUtils.isNotBlank(classFieldTypeAnnotation.regex())){
boolean matches = this.regexCheck((String) obj,classFieldTypeAnnotation.regex());
if(!matches){
if(StringUtils.isNotBlank(classFieldTypeAnnotation.msg())){
errorTips.add(classFieldTypeAnnotation.msg());
}else {
errorTips.add(chineseName[0] + "字段格式错误");
}
}
}
}
} catch (IllegalAccessException e) {
log.info("导入失败",e);
errorTips.add("导入失败");
}
return errorTips;
}
public boolean isNumeric(String str) {
return str.matches("\\d+");
}
/**
* 正则表达式校验
* @param ipAddress
* @param regex
* @return
*/
public boolean regexCheck(String ipAddress,String regex) {
if ((ipAddress != null) && (!ipAddress.isEmpty())) {
return Pattern.matches(regex, ipAddress);
}
return false;
}
public boolean doubleCheck(String str) {
return str.matches("\\d{1,6}(\\.?)(\\d{0,4})");
}
}
2.自定义一个注解
package com.yj.annotation;
import java.lang.annotation.*;
/**
* 字段注解
* @author hjc
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ImportFieldCheckAnnotation {
/**
* 数字类型
*/
boolean isNumber() default false;
boolean isDouble() default false;
/**
* 字符长度
* @return
*/
int length() default 0;
/**
* 最大值
* @return
*/
int max() default 0;
/**
* 最小值
* @return
*/
int min() default 0;
/**
* 字段是否可以为空
* @return
*/
boolean permitNull() default false;
/**
* 字典值校验
* @return
*/
String dictCode() default "";
/**
* 正则表达式校验
*/
String regex() default "";
/**
* 提示
*/
String msg() default "";
}
3.创建实体类,使用注解
@Data
public class Cloud {
@ApiModelProperty(value = "区域")
@ExcelProperty(value = "区域", index = 0)
@ImportFieldCheckAnnotation(length = 100,dictCode = "region")
private String regionName;
@ApiModelProperty(value = "业务")
@ExcelProperty(value = "业务", index = 1)
@ImportFieldCheckAnnotation(length = 100)
private String institution;
@ExcelProperty(value = "名称", index = 2)
@ApiModelProperty(value = "名称")
@ImportFieldCheckAnnotation(length = 100)
private String Name;
@ExcelProperty(value = "实例", index = 3)
@ApiModelProperty(value = "实例")
@ImportFieldCheckAnnotation(permitNull = true,length = 100)
private String instance
@ExcelProperty(value = "天数", index = 4)
@ApiModelProperty(value = "天数")
@ImportFieldCheckAnnotation(permitNull = true,isNumber = true,max = 10000)
private String testDays;
}
4.Controller
public ResultVO<ExcelReturnVO> upload(MultipartFile file) {
ExcelReturnVO excelReturnVO = new ExcelReturnVO();
String fileName = ExcelUtils.fileTypeCheck(file);
Workbook wb = null;
String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
try {
InputStream fin = file.getInputStream();
if ("xls".equals(suffix)) {
wb = new HSSFWorkbook(fin);
} else if ("xlsx".equals(suffix)) {
wb = new XSSFWorkbook(fin);
}
String[] columnName =
{"区域", "业务", "名称", "时间", "实例", "天数"};
//判断导入表头是否正确
ExcelUtils.verificationExcelHeadLine(1, wb, columnName);
//读取数据转换成实体类列表
//EasyExcel.read(file.getInputStream(),Cloud.class)
//从第二行开始读
//.headRowNumber(2)
//第一个表
//.sheet(0).doRead();
2.效验操作
private void checkData(Cloud inputEntity, List<CloudError> errorInfoList) {
List<String> errorTips = new ArrayList<>();
释放时间格式错误 格式:YYYY/MM/DD ");
}
//数据校验
//获取Cloud类 的所有属性
Field[] declaredFields = inputEntity.getClass().getDeclaredFields();
ExcelUtils<Cloud > excelUtils=new ExcelUtils();
//使用自定义的方法对所有属性进行解析效验
excelUtils.importCheck(declaredFields, inputEntity, errorTips);