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*/@NotNullpublic 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);