写在前面的话:
调用本工具,将excel数据转换为List<对象>,用户自行对list做业务处理。
备注:1、由于本方法为通用方法,故不支持涉及到行or列合并的情况。
-- 你永远不要去低估一颗冠军的心,恭喜龙队
一、前情提要
二、结果示例
1、代码调用示例(Controller)
@ApiOperation("导出") public JsonResult<List<StudentEntity>> importByExcel(MultipartFile file) { List<StudentEntity> studentList = ExcelUtils.importByExcel(file, 0, 0, 2, 1, StudentEntity.class); // Omitted code // ... return JsonResult.success(studentList); }
2、结果示例
3、目标excel示例:
三、实现原理
1、定位要导入的sheet页。
2、定位目标sheet中,有效数据的起始行、起始列。
3、定位目标sheet中,字段名所在行。
4、遍历excel中的行数据,通过反射比对,对实体进行赋值。
四、pom依赖引入
<commons.lang3.version>3.8.1</commons.lang3.version> <poi.version>3.17</poi.version> <commons.io.version>2.6</commons.io.version> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons.lang3.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>${poi.version}</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>${poi.version}</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>${commons.io.version}</version> </dependency>
五、工具源码
1、工具核心代码
/** * 导入Excel文件 * 本方法暂不支持一次性导入 多sheet页 数据。 * 若需导入多sheet页,请调用方多次调用。 * * @param excelFile 上传的excel文件 * @param sheetNum 导入excel的sheet页编号。如:导入该excel的sheet1页的数据,则该值为0。通常都为0。【sheet页起始值为0】 * @param validColumnNum 有效的起始列。如:excel从A列开始即为字段数据,则该值为0。【列的起始值为0】 * @param validRowNum 有效的起始行。如:excel从3行开始即为需导入数据,则该值为2。【行的起始值为0,一般第一行为中文标题,第二行为字段名】 * @param fieldsNum 字段所在行。如:excel第2行为字段,则该值为1【行的起始值为0,一般第一行为中文标题,第二行为字段名】 * @param clazz T.class * @return 组装好的list 调用方执行入库即可 */ public static <T> List<T> importByExcel(MultipartFile excelFile, Integer sheetNum, Integer validColumnNum, Integer validRowNum, Integer fieldsNum, Class<T> clazz) { if (!(excelFile.getOriginalFilename().endsWith("xls") || excelFile.getOriginalFilename().endsWith("xlsx"))) { // 文件格式有误 log.error("文件格式有误!"); throw new RuntimeException(Constants4Api.FILE_IS_NOT_EXCEL); } HSSFWorkbook workbook = null; try { InputStream inputStream = excelFile.getInputStream(); workbook = new HSSFWorkbook(inputStream); } catch (IOException e) { log.error("获取上传文件的输入流失败!请检查后端文件接收变量名与前端变量名是否一致!"); throw new RuntimeException(Constants4Api.EXCEL_IS_ERROR); } HSSFSheet sheet = workbook.getSheetAt(sheetNum); Integer lastColumnNum = Integer.valueOf(sheet.getRow(0).getLastCellNum()); // 获取数据类型字段集合 List<String> fieldsStrList = getFieldsStrList(sheet.getRow(fieldsNum), validColumnNum, lastColumnNum); List<T> tList = new ArrayList<>(); for (Row row : sheet) { T t = null; try { t = clazz.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } // 过滤掉无效的行 int rowNum = row.getRowNum(); if (rowNum < validRowNum) { continue; } for (int colNum = validColumnNum; colNum < lastColumnNum; colNum++) { // 判断cell类型 Object value = getValueThrowDifferentType(row.getCell(colNum)); loadValue2Entity(t, clazz, value, fieldsStrList.get(colNum - validColumnNum)); } tList.add(t); } return tList; }
2、辅助代码
/** * 获取所有字段名 * * @param row 字段所在行 * @param validColumnNum 有效起始列 * @param lastColumnNum 有效结束列 * @return 字段名List */ private static List<String> getFieldsStrList(Row row, Integer validColumnNum, Integer lastColumnNum) { List<String> fieldsStrList = new ArrayList<>(); for (int i = validColumnNum; i < lastColumnNum; i++) { String fields = row.getCell(i).getStringCellValue(); fieldsStrList.add(fields); } return fieldsStrList; }
/** * 根据 数据类型 取 单元格的值 * * 备注: 若后续有其他数据类型,则在此处添加对应的处理方式即可 * * @param cell 单元格 * @return value */ private static Object getValueThrowDifferentType(Cell cell) { Object value = null; if (!Objects.isNull(cell)) { CellType cellType = cell.getCellTypeEnum(); switch (cellType) { case NUMERIC: value = cell.getNumericCellValue(); break; case STRING: value = cell.getStringCellValue(); break; case BOOLEAN: value = cell.getBooleanCellValue(); break; default: break; } } return value; }
/** * 以反射的方式 给实体类赋值 * * @param t 实体 * @param clazz t.class * @param value 值 * @param objFiled 目标字段名 * @param <T> 泛型 */ private static <T> void loadValue2Entity(T t, Class<T> clazz, Object value, String objFiled) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (objFiled.equals(field.getName())) { try { if (!Objects.isNull(value)) { setValueThrowDifferentType(field, t, value); } } catch (Exception e) { log.error("反射赋值失败!当前字段:" + objFiled + ", 当前值:" + value , e); throw new RuntimeException(Constants4Api.EXCEL_BIND_ENTITY_ERROR); } } } }
/** * 根据不同的数据类型 对 实体 进行赋值 * * @param field 字段名 * @param t 实体 * @param value 值 * @param <T> 泛型 * @throws Exception exception */ private static <T> void setValueThrowDifferentType(Field field, T t, Object value) throws Exception { String fieldType = field.getGenericType().toString(); field.setAccessible(true); switch (fieldType) { case "class java.lang.Integer": field.set(t, Integer.parseInt(value.toString().split("\\.")[0])); break; case "class java.lang.Double": field.set(t, Double.parseDouble(value.toString())); break; case "class java.lang.Boolean": Boolean flag = null; if ("是".equals(value.toString())) { flag = true; } else if ("否".equals(value.toString())) { flag = false; } else { flag = Boolean.parseBoolean(value.toString()); } field.set(t, flag); break; case "class java.lang.String": String[] tempArr = value.toString().split("\\."); if (tempArr.length == 2 && tempArr[1].equals("0")) { // 纯数字,会自动加 .0 field.set(t, tempArr[0]); } else { field.set(t, StringUtils.isEmpty(value.toString()) ? "" : value.toString()); } break; case "class java.math.BigDecimal": field.set(t, new BigDecimal(value.toString())); break; case "class java.util.Date": field.set(t, HSSFDateUtil.getJavaDate(Double.parseDouble(value.toString()))); break; default: log.error("待添加的转换类:" + fieldType); break; } }
3、示例中是student实体类
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("sys_student") @ApiModel("sys_student表实体类") public class StudentEntity extends Model<StudentEntity> { private static final long serialVersionUID = 1L; @TableId(value = "dept_id", type = IdType.UUID) @ApiModelProperty(value = "学生编号:主键id") private String studentId; @ApiModelProperty(value = "姓名") private String studentName; @ApiModelProperty(value = "性别:str") private String gender; }