赞
踩
基于poi、注解类加反射实现自定义文件表头的导入功能、将表头名写入到实体类的注解类上,就可以实现自定义文件的导入功能工具类;
这是导入的文件数据格式,只读取并导入红色区域的内容
使用到的依赖
<!-- excel工具 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.13</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.13</version> </dependency> <!--工具类--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.3</version> </dependency> <!--实体类get set 构造方法注解--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> <!--常用工具类 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <!-- io常用工具类 --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> <!-- 文件上传工具类 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
一共需要三个java文件,工具类和两个注解类
import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.study.test.util.note.Description; import com.study.test.util.note.Excel; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.CharUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.poi.hssf.usermodel.HSSFDateUtil; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.math.BigDecimal; import java.text.DecimalFormat; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 注释: excel工具类 * * @author yangyongzhuo 2022/11/17 19:59 */ @Slf4j public class ExcelUtils { private final static String EXCEL2003 = "xls"; private final static String EXCEL2007 = "xlsx"; /** * 通过clsss,读取excel里面的数据,只要表头与Excel里面的notes一致就可以,不要关注顺序 * * @param file 文件 * @param clsss vo * @return vo集合 * @return java.util.List<T> * @author yangyongzhuo 2022/11/17 19:59 */ public static <T> List<T> readExcel(MultipartFile file, Class<T> clsss) { //开始执行时间 long start = System.currentTimeMillis(); Workbook workbook = null; //返回数据对象 List<T> dataList = null; //先判断文件命名是否正确,再判断文件是哪种类型 String fileName = file.getName(); if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$")) { log.error("上传文件格式不正确"); throw new RuntimeException("Excel命名格式不正确!"); } try { InputStream is = file.getInputStream(); if (fileName.endsWith(EXCEL2007)) { workbook = new XSSFWorkbook(is); } if (fileName.endsWith(EXCEL2003)) { workbook = new HSSFWorkbook(is); } if (ObjectUtil.isEmpty(workbook)) { throw new RuntimeException("Excel格式不正确,未获取工作空间!"); } dataList = new ArrayList<>(); //通过反射获取注释类上的数据下标值 Description annotation = clsss.getAnnotation(Description.class); //数据位置因为poi读取下标是从0开始,所以要减1 int dataIndex = annotation.dataIndex() - 1; //是否忽略空行 boolean ifNull = annotation.ifNull(); // 类映射 注解,拿到文件字段名称 Map<String, List<Field>> classMap = new HashMap<>(); List<Field> fields = Stream.of(clsss.getDeclaredFields()).collect(Collectors.toList()); fields.forEach(field -> { // Excel excel = field.getAnnotation(Excel.class); if (ObjectUtil.isEmpty(excel)) { return; } String notes = excel.notes(); if (StrUtil.isEmpty(notes)) { return; } if (!classMap.containsKey(notes)) { classMap.put(notes, new ArrayList<>()); } field.setAccessible(true); classMap.get(notes).add(field); }); // 获取字段对应的列号 Map<Integer, List<Field>> reflectionMap = new HashMap<>(16); // 默认读取第一个sheet Sheet sheet = workbook.getSheetAt(0); // boolean firstRow = true; for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); // 表头区域获取字段对应的列 if (i < dataIndex) { for (int j = row.getFirstCellNum(); j <= row.getLastCellNum(); j++) { Cell cell = row.getCell(j); String cellValue = getCellValue(cell); if (classMap.containsKey(cellValue)) { reflectionMap.put(j, classMap.get(cellValue)); } } firstRow = false; } else { // 忽略空白行 if (row == null) { continue; } try { T t = clsss.newInstance(); // 判断是否为空白行 boolean allBlank = true; for (int j = row.getFirstCellNum(); j <= row.getLastCellNum(); j++) { if (reflectionMap.containsKey(j)) { Cell cell = row.getCell(j); String cellValue = getCellValue(cell); if (StringUtils.isNotBlank(cellValue)) { allBlank = false; } List<Field> fieldList = reflectionMap.get(j); fieldList.forEach(x -> { try { handleField(t, cellValue, x); } catch (Exception e) { log.error(String.format("reflect field:%s value:%s exception!", x.getName(), cellValue), e); } }); } } if (!allBlank) { dataList.add(t); } else { //if is null return this import code block if (ifNull) { return dataList; } log.warn(String.format("row:%s is blank ignore!", i)); } } catch (Exception e) { log.error(String.format("parse row:%s exception!", i), e); } } } } catch (Exception e) { log.error(String.format("parse excel exception!"), e); } finally { if (workbook != null) { try { workbook.close(); } catch (Exception e) { log.error(String.format("parse excel exception!"), e); } } } long end = System.currentTimeMillis(); log.info("read excel cost {}s", (end - start) / 1000); return dataList; } /** * 注释: 获取数据原始类型 * * @param t * @param value * @param field * @return void * @author yangyongzhuo 2022/11/25 13:25 */ private static <T> void handleField(T t, String value, Field field) throws Exception { Class<?> type = field.getType(); if (type == null || type == void.class || StringUtils.isBlank(value)) { return; } if (type == Object.class) { field.set(t, value); // 数字类型 } else if (type.getSuperclass() == null || type.getSuperclass() == Number.class) { if (type == int.class || type == Integer.class) { field.set(t, NumberUtils.toInt(value)); } else if (type == long.class || type == Long.class) { field.set(t, NumberUtils.toLong(value)); } else if (type == byte.class || type == Byte.class) { field.set(t, NumberUtils.toByte(value)); } else if (type == short.class || type == Short.class) { field.set(t, NumberUtils.toShort(value)); } else if (type == double.class || type == Double.class) { field.set(t, NumberUtils.toDouble(value)); } else if (type == float.class || type == Float.class) { field.set(t, NumberUtils.toFloat(value)); } else if (type == char.class || type == Character.class) { field.set(t, CharUtils.toChar(value)); } else if (type == boolean.class) { field.set(t, BooleanUtils.toBoolean(value)); } else if (type == BigDecimal.class) { field.set(t, new BigDecimal(value)); } } else if (type == Boolean.class) { field.set(t, BooleanUtils.toBoolean(value)); } else if (type == Date.class) { field.set(t, value); } else if (type == String.class) { field.set(t, value); } else { Constructor<?> constructor = type.getConstructor(String.class); field.set(t, constructor.newInstance(value)); } } /** * 注释: 获取数据类型 * * @param cell * @return java.lang.String * @author yangyongzhuo 2022/11/25 13:26 */ private static String getCellValue(Cell cell) { if (cell == null) { return ""; } int cellType = cell.getCellType(); if (cellType == Cell.CELL_TYPE_FORMULA) { // 表达式类型 cellType = cell.getCachedFormulaResultType(); } if (cellType == Cell.CELL_TYPE_NUMERIC) { if (HSSFDateUtil.isCellDateFormatted(cell)) { Date date = HSSFDateUtil.getJavaDate(cell.getNumericCellValue()); return DateUtil.format(date, "yyyy-MM-dd"); } else { return new DecimalFormat("#.######").format(cell.getNumericCellValue()); } } else if (cellType == Cell.CELL_TYPE_STRING) { return StringUtils.trimToEmpty(cell.getRichStringCellValue() + ""); } else if (cellType == Cell.CELL_TYPE_BLANK) { return ""; } else if (cellType == Cell.CELL_TYPE_BOOLEAN) { return String.valueOf(cell.getBooleanCellValue()); } else if (cellType == Cell.CELL_TYPE_ERROR) { return "ERROR"; } else { return cell.toString().trim(); } } }
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 注释: 读取信息 * * @author yangyongzhuo 2022/11/17 15:41 */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Description { String description() default ""; /** 读到空白行处理方式 true 结束此sheet页导入,false继续导入 */ boolean ifNull() default true; /** 表头的位置 */ int headerIndex() default 0; /** 数据行的下表位置 */ int dataIndex() default 1; /** 起始sheet页的下标 */ int startSheetIndex() default 0; }
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 注释: 备注注解 * * @author yangyongzhuo 2022/11/18 10:56 */ @Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Excel { String value() default ""; String notes() default ""; }
需要注意注解里面的notes是你要导入的字段名称,可以参考我的写法,
/** * 注释: 导入对象 * @Description: dataIndex 数据的行号, * ifNull默认是true 如果有空行就终止导入,false就是忽略空行继续导入 * * @author yangyongzhuo 2022/11/17 15:42 */ @Description(dataIndex = 9,ifNull = false) @Data public class ReadExcelVo { @Excel(notes = "监测点编号") private String param1; @Excel(notes = "上次累计") private String param2; @Excel(notes = "本次累计") private String param3; @Excel(notes = "本次变化") private String param4; @Excel(notes = "变化速率(mm/d)") private String param5; @Excel(notes = "速率(mm/d)") private String param6; @Excel(notes = "累计值 (mm)") private String param7; @Excel(notes = "对应位置\n" + "(区域)") private String param8; @Excel(notes = "备注") private String param9; }
/** * 注释: 文件测试 * * @author yangyongzhuo 2022/11/17 20:01 */ @RestController @RequestMapping("/test/controller") public class TestController { @PostMapping("/testImport") public void testImport(@RequestParam("file") MultipartFile file) { List<ReadExcelVo> readExcelVos = ExcelUtils.readExcel(file, ReadExcelVo.class); //打印导入数据 readExcelVos.forEach(System.err::println); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。