Spring boot poi 导入,适应根据表头名导入,合并单元格导入

添加依赖

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.0.0</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.0.0</version>
</dependency>

代码实现

导入注解类 ExcelImportAnnotation

/**
 * @author hbj
 * @Date 2021/5/17 15:09
 * @Description Excel导入注解
 * @Modified By
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelImportAnnotation {
    /**
     * 表头中文
     *
     * @return
     */
    String titleName() default "";

    /**
     * 是否允许空值 ,默认不允许
     *
     * @return
     */
    boolean required() default false;

    /**
     * 时间格式
     *
     * @return
     */
    String format() default "yyyy-MM-dd";

    /**
     * 多元素判断,每个元素用,隔开,如:是,否,对应的values值为1,0
     *
     * @return
     */
    String judge_items() default "";
    /**
     * 多元素判断值,每个元素用,隔开, 如:1,0
     *
     * @return
     */
    String judge_values() default  "";

    /**
     * 默认值
     *
     * @return
     */
    String judge_defaultValue() default "";

    /**
     * 行号
     *
     * @return
     */
    boolean isRow() default false;
}

导入映射类

/**
 * @author hbj
 * @Date 2021/5/28 10:21
 * @Description
 * @Modified By
 */
@Data
public class ImportDto {
    @ApiModelProperty(value = "Excel行号")
    @ExcelImportAnnotation(isRow = true)
    private Integer row;

    @ApiModelProperty(value = "班级")
    @ExcelImportAnnotation(titleName = "班级",required = true)
    private String grade;

    @ApiModelProperty(value = "学生姓名")
    @ExcelImportAnnotation(titleName = "学生姓名",required = true)
    private Date studentName;

    @ApiModelProperty(value = "语文分数")
    @ExcelImportAnnotation(titleName = "语文分数",required = true)
    private BigDecimal chineseScore;

	@ApiModelProperty(value = "数学分数")
    @ExcelImportAnnotation(titleName = "数学分数",required = true)
    private BigDecimal mathScore;

    @ApiModelProperty(value = "英语分数")
    @ExcelImportAnnotation(titleName = "英语分数",required = true)
    private BigDecimal englishScore;
    
}

导入异常处理类

/**
 * @author hbj
 * @Date 2021/6/22 15:29
 * @Description
 * @Modified By
 */
public class ImportException extends RuntimeException{
    public ImportException(String msg) {
        super(msg);
    }
}

导入工具类

/**
 * @author hbj
 * @Date 2021/5/17 15:38
 * @Description
 * @Modified By
 */
public class ExcelUtil {

    /**
     * xls 后缀
     */
    public static final String XLS = "xls";
    /**
     * xlsx 后缀
     */
    public static final String XLS_X = "xlsx";

    /**
     * 传入文本对象输出list集合(导入),只导入第一页(sheet1)
     *
     * @param file  流文件
     * @param clazz 要转义成的类对象
     * @return
     */
    public static <T> List<T> importExcelSheet1(MultipartFile file, Class<T> clazz) throws Exception {
        // 检查文件
        Workbook workbook = getWorkBook(file);
        List<T> list = new ArrayList<T>();
        checkFile(file);
        // 获得HSSFWorkbook工作薄对象
        if (workbook != null) {
            // 获得当前sheet工作表
            Sheet sheet = workbook.getSheetAt(0);
            //获取表头
            Map<String,Integer> titleMap=getExcelSheetTitle(sheet);
            //获取对应表头对应的实体字段集合
            Map<Integer,Field> fieldMap= getFieldMap(clazz,titleMap);
            //获取所有合并的单元格
            Map<String,Cell> combineCellMap=getCombineCell(sheet);

            // 获取当前sheet工作表的列总数
            int firstLine = sheet.getRow(0).getPhysicalNumberOfCells();
            // 获得当前sheet的开始行
            int firstRowNum = sheet.getFirstRowNum()+1;//不用读取表头
            // 获得当前sheet的结束行
            int lastRowNum = sheet.getLastRowNum();
            // 循环所有行
            for (int rowNum = firstRowNum; rowNum <= lastRowNum; rowNum++) {
                // 获得当前行
                Row row = sheet.getRow(rowNum);
                if (row == null) {
                    continue;
                }
                Object obj = clazz.newInstance();
                //循环所有列得到单元格
                for (int cellNum = 0; cellNum < firstLine; cellNum++) {
                    // 取出对应对象属性
                    Field field=fieldMap.get(cellNum);
                    if(field==null){
                        continue;
                    }
                    Cell cell = row.getCell(cellNum);
                    //是否合并单元格
                    if(!combineCellMap.isEmpty()&&combineCellMap.containsKey(rowNum+"#"+cellNum)){
                        cell=combineCellMap.get(rowNum+"#"+cellNum);
                    }
                    //录入行号
                    Field mfield=fieldMap.get(-1);//行号
                    if(mfield!=null){
                        if (!mfield.isAccessible()) {
                            mfield.setAccessible(true);
                        }
                        if(mfield.get(obj)==null&&cell!=null){
                            mfield.set(obj,cell.getRowIndex());
                        }
                    }
                    if(cell!=null){
                        Object value=getCellValue(cell);
                        // 根绝类型 实体类赋值
                        createBean(field, obj, value,rowNum);
                    }
                }
                if (rowNum == 0) {
                    // 表头不做记录
                    continue;
                }
                if(checkObjFieldIsNotNull(obj)) {
                    //必填校验
                    checkRequired(obj,rowNum);
                    list.add((T) obj);
                }else{//有一行是空的就默认结束行
                    return list;
                }
            }
        }
        return list;
    }

    private static void checkRequired(Object obj,int rowNum ) throws IllegalAccessException {

        //获取对象总数量
        Field[] fields = obj.getClass().getDeclaredFields();
        if (fields == null || fields.length == 0) {
            return;
        }
        StringJoiner joiner=new StringJoiner(",");
        for (Field field : fields) {
            if(!field.isAccessible()){
                field.setAccessible(true);
            }
            ExcelImportAnnotation annotation=field.getAnnotation(ExcelImportAnnotation.class);
            Object obj2=field.get(obj);
            if(annotation!=null && annotation.required() && obj2==null){//必填
                joiner.add(annotation.titleName());
            }else if(obj2!=null&&annotation!=null&& StringUtils.isNotBlank(annotation.judge_items())){
                List<String> items=Arrays.asList(annotation.judge_items().split(","));
                List<String> values=Arrays.asList(annotation.judge_values().split(","));
                int index=items.indexOf(obj2.toString());
                if(index>-1&&values.size()>index){
                    field.set(obj,values.get(index));
                }
            }
        }
        if(joiner.length()>0){
            rowNum++;
            throw new ImportException("第"+rowNum+"行【"+joiner+"】不能为空");
        }
    }

    /**
     * 获取表头
     * @param sheet
     * @return
     */
    private static Map<String, Integer> getExcelSheetTitle(Sheet sheet) {
        Map<String, Integer> titleMap=new HashMap<>();
        Row row = sheet.getRow(0);
        // 获取当前sheet工作表的列总数
        int allLine = row.getPhysicalNumberOfCells();
        for (int cellNum = 0; cellNum < allLine; cellNum++) {
            Cell cell = row.getCell(cellNum);
            if(cell!=null&&StringUtils.isNotBlank(cell.getStringCellValue())){
                titleMap.put(cell.getStringCellValue(),cellNum);
            }
        }
        return titleMap;
    }

    /**
     * 创建工作簿
     * @param file
     * @return
     */
    private static Workbook getWorkBook(MultipartFile file) {
        // 获得文件名
        String fileName = file.getOriginalFilename();
        // 创建Workbook工作薄对象,表示整个excel
        Workbook workbook = null;
        // 获取excel文件的io流
        InputStream is = null;
        try {
            is = file.getInputStream();
            // 根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象
            if (fileName.endsWith(XLS)) {
                // 2003
                workbook = new HSSFWorkbook(is);
            } else if (fileName.endsWith(XLS_X)) {
                // 2007
                workbook = new XSSFWorkbook(is);
            }
        } catch (IOException e) {
            throw new ImportException("excel 转换 HSSFWorkbook 异常!");
        }
        return workbook;
    }

    /**
     * 检查文件
     *
     * @param file
     * @throws IOException
     */
    private static void checkFile(MultipartFile file) {
        // 判断文件是否存在
        if (null == file) {
            throw new ImportException("文件不存在");
        }
        // 获得文件名
        String fileName = file.getOriginalFilename();
        // 判断文件是否是excel文件
        if (!fileName.endsWith(XLS) && !fileName.endsWith(XLS_X)) {
            throw new ImportException( fileName + "不是excel文件");
        }
    }

    /**
     * 获取实体属性集合
     *
     * @param clazz 对象
     * @param titleMap  表头数组key->表头名,value->列数
     * @return
     */
    private static Map<Integer,Field> getFieldMap(Class clazz,Map<String,Integer> titleMap) {
        Map<Integer,Field> fieldMap=new HashMap<>();
        //获取对象总数量
        Field[] fields = clazz.getDeclaredFields();
        if (fields == null || fields.length == 0) {
            return fieldMap;
        }
        Class<ExcelImportAnnotation> cls=ExcelImportAnnotation.class;
        for (Field field : fields) {
            if(field.isAnnotationPresent(cls)){
                if (StringUtils.isNotBlank(field.getAnnotation(cls).titleName())) {
                    String key=field.getAnnotation(cls).titleName();
                    if(titleMap.get(key)!=null){
                        fieldMap.put(titleMap.get(key),field);
                    }
                }else if(field.getAnnotation(cls).isRow()){//行号
                    fieldMap.put(-1,field);
                }
            }

        }
        return fieldMap;
    }

    /**
     * 根据实体类型 赋值数据
     *
     * @param field
     * @param newInstance
     * @param value
     * @param <T>
     */
    private static <T> void createBean(Field field, T newInstance, Object value,int rowNum) {
        if(field==null){
            return;
        }
        if (!field.isAccessible()) {
            field.setAccessible(true);
        }
        try {
            if (value == null|| StringUtils.isBlank(String.valueOf(value))) {
                field.set(newInstance, null);
            } else if (Long.class.equals(field.getType())) {
                field.set(newInstance, Long.valueOf(String.valueOf(value)));
            } else if (String.class.equals(field.getType())) {
                field.set(newInstance, String.valueOf(value));
            } else if (Integer.class.equals(field.getType())) {
                field.set(newInstance, Integer.valueOf(String.valueOf(value)));
            } else if (Date.class.equals(field.getType())) {
                SimpleDateFormat sdf = new SimpleDateFormat(field.getAnnotation(ExcelImportAnnotation.class).format());
                if (value instanceof Date)   {
                    field.set(newInstance, sdf.parse(sdf.format(value)));
                } else {
                    field.set(newInstance, sdf.parse(value.toString()));
                }
            } else if (Boolean.class.equals(field.getType())) {
                field.set(newInstance, (Boolean) value);
            } else if (Double.class.equals(field.getType())) {
                field.set(newInstance, Double.valueOf(String.valueOf(value)));
            } else if (Float.class.equals(field.getType())) {
                field.set(newInstance, Float.valueOf(String.valueOf(value)));
            } else if (BigDecimal.class.equals(field.getType())) {
                field.set(newInstance, new BigDecimal(String.valueOf(value)));
            } else {
                field.set(newInstance, value);
            }
        } catch (Exception e) {
            rowNum++;
            throw new ImportException("【excel导入】excel实体转换异常!第"+rowNum+"行字段【"
                    + field.getAnnotation(ExcelImportAnnotation.class).titleName() + "】,值(" + value + ")");
        }
    }

    /**
     * 列转化值
     *
     * @param cell 列值
     * @throws IOException
     */
    private static Object getCellValue(Cell cell) {
        if (cell == null) {
            return null;
        }
        Object cellValue;
        // 把数字当成String来读,避免出现1读成1.0的情况
        // 判断数据的类型
        switch (cell.getCellTypeEnum()) {
            case NUMERIC:

                if (DateUtil.isValidExcelDate(cell.getNumericCellValue())) {
                    CellStyle style = cell.getCellStyle();
                    if (style == null) {
                        return false;
                    }
                    int formatIndex = style.getDataFormat();
                    String formatString = style.getDataFormatString();
                    boolean isDate = DateUtil.isADateFormat(formatIndex, formatString);
                    if (isDate) {          Date date = cell.getDateCellValue();
                        return date;
                    }
                }
                if ((long) cell.getNumericCellValue() != cell.getNumericCellValue()) {
                    // double 类型
                    cellValue = new BigDecimal(String.valueOf(cell.getNumericCellValue()));
                } else {
                    cellValue = (long)cell.getNumericCellValue();
                }
                break;
            // 字符串
            case STRING:
                cellValue = String.valueOf(cell.getStringCellValue());
                break;

            // Boolean
            case BOOLEAN:
                cellValue = String.valueOf(cell.getBooleanCellValue());
                break;
            // 公式
            case FORMULA:
                cellValue = String.valueOf(cell.getCellFormula());
                break;
            // 空值
            case BLANK:
                cellValue = null;
                break;
            // 故障
            case ERROR:
                cellValue = "非法字符";
                break;
            default:
                cellValue = "未知类型";
                break;
        }
        return cellValue;
    }

    private static boolean checkObjFieldIsNotNull(Object obj){
        try {
            Class<ExcelImportAnnotation> cls=ExcelImportAnnotation.class;
            for (Field f : obj.getClass().getDeclaredFields()) {
                if(f.isAnnotationPresent(cls)&&f.getAnnotation(cls).isRow()){
                   continue;
                }
                f.setAccessible(true);
                if (f.get(obj) != null) {
                    return true;
                }
            }
        }catch (IllegalAccessException e){

        }
        return false;
    }

    /**
     * 获取sheet中合并的单元格个数,并返回单元格list
     * @param sheet
     * @return Map<String,Object> key->row+"#"+cell
     */
    public static Map<String,Cell> getCombineCell(Sheet sheet)
    {
        Map<String,Cell> map = new HashMap<>();
        //获得一个 sheet 中合并单元格的数量
        int sheetmergerCount = sheet.getNumMergedRegions();
        //遍历所有的合并单元格
        for(int i = 0; i<sheetmergerCount;i++)
        {
            //获得合并单元格保存进list中
            CellRangeAddress ca = sheet.getMergedRegion(i);
            int firstColumn = ca.getFirstColumn();
            int lastColumn = ca.getLastColumn();
            int firstRow = ca.getFirstRow();
            int lastRow = ca.getLastRow();
            for(int r=firstRow;r<=lastRow;r++){
                for(int c=firstColumn;c<=lastColumn;c++){
                    map.put(r+"#"+c,sheet.getRow(firstRow).getCell(firstColumn));
                }
            }
        }
        return map;
    }

Controller

/**
 * @author bojin.he
 * @Date 2021/6/22 15:47
 * @Description
 * @Modified By
 */
@RestController
@RequestMapping("/excel")
public class ExcelCtrl {

    @PostMapping("/excelImport")
    public List<ImportDto> excelImport(@RequestParam("file") MultipartFile file) throws Exception {
        List<ImportDto> importDtos= ExcelUtil.importExcelSheet1(file,ImportDto.class);
        return importDtos;
    }
}

excel内容

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值