小言_互联网的博客

(三)poi配合注解实现导入导出功能

313人阅读  评论(0)

以下列出实现导入功能需要的各个方法,循序渐进

  /**
     * 创建Workbook工作薄对象
     *
     * @param file 上传的文件
     * @return
     */
    public static Workbook getWorkBook(MultipartFile file) {
        //获得文件名
        String fileName = file.getOriginalFilename();
        //创建Workbook工作薄对象,表示整个excel
        Workbook workbook = null;
        try {
            //获取excel文件的io流
            InputStream is = file.getInputStream();
            //根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象
            if (fileName.endsWith("xls")) {
                //2003,填充导入excel文件数据进入工作簿
                workbook = new HSSFWorkbook(is);
            } else if (fileName.endsWith("xlsx")) {
                //2007 及2007以上,填充导入excel文件数据进入工作簿
                workbook = new XSSFWorkbook(is);
            }
        } catch (IOException e) {
            throw new BusinessException("上传文件生成工作簿出错");
        }
        return workbook;
    }
 /**
     * excel表格中数据的转换
     *
     * @param cell
     * @return
     */
    public static String getCellValue(Cell cell) {
        String cellValue = "";
        if (Objects.isNull(cell)) {
            return cellValue;
        }
        int cellType = cell.getCellType();
        //根据数据的类型进行相应转换
        switch (cellType) {
            //数字类型
            case Cell.CELL_TYPE_NUMERIC:
                cellValue = String.valueOf(cell.getNumericCellValue());
                break;
            //字符串类型
            case Cell.CELL_TYPE_STRING:
                cellValue = String.valueOf(cell.getStringCellValue());
                break;
            //布尔类型
            case Cell.CELL_TYPE_BOOLEAN:
                cellValue = String.valueOf(cell.getBooleanCellValue());
                break;
            //公式类型
            case Cell.CELL_TYPE_FORMULA:
                cellValue = String.valueOf(cell.getCellFormula());
                break;
            //空值类型
            case Cell.CELL_TYPE_BLANK:
                cellValue = "";
                break;
            //错误类型
            case Cell.CELL_TYPE_ERROR:
                cellValue = "错误类型";
                break;
            default:
                cellValue = "";
                break;
        }
        return cellValue;
    }
/**
     * 对字段做类型转换(类型和实体一一对应,如果有其他类型,可以对该方法进行扩展)
     *
     * @param fieldValue 字段值
     * @param fieldType  字段类型
     * @param fieldName  字段名称
     * @param line       excel行数
     * @return
     */
    public static Object typeCast(String fieldValue, Class fieldType, String fieldName, String line) {
        Object value = fieldValue;
        if (Objects.nonNull(value)) {
            //判断字段是否是日期类型
            if (fieldType.isAssignableFrom(Date.class)) {
                value = DateUtils.parseDate(fieldValue);
                if (Objects.isNull(value)) {
                    //抛出自定义的业务异常,给前端显示,提示用户的第几行哪个字段出了错
                    throw new BusinessException("第"+line+"行"+fieldName+"格式有误");
                }
            }
            //判断字段是否是数字类型
            if (fieldType.isAssignableFrom(BigDecimal.class)) {
                try {
                    value = new BigDecimal(fieldValue);
                } catch (Exception e) {
                 //抛出自定义的业务异常,给前端显示,提示用户的第几行哪个字段出了错
                    throw new BusinessException("第"+line+"行"+fieldName+"格式有误");
                }
            }
        }
        return value;
    }
 /**
     * 把map转换成实体
     *
     * @param mapList 数据的map集合
     * @param pojoClass 数据实例类型
     * @return
     * @throws Exception
     */
    public static List<Object> convertMapToEntity(List<Map<String, String>> mapList, Class<?> pojoClass) throws Exception {
         //对象集合
        List<Object> objectList = new ArrayList<>();
        //集合初始化行数(方便之后错误提示可以具体到行数)
        int line = 3;
        //遍历mapList
        for (Map<String, String> entityMap : mapList) {
            //实例化
            Object object = pojoClass.newInstance();
            //反射取得类各个字段
            Field[] fields = pojoClass.getDeclaredFields();
            //遍历map
            for (Map.Entry<String, String> entry : entityMap.entrySet()) {
                String key = entry.getKey();
                //遍历对象字段
                for (Field field : fields) {
                    //取得注解属性和字段名称
                    Excel excel = field.getAnnotation(Excel.class);
                    Class fieldType = field.getType();
                    if (Objects.nonNull(excel)) {
                        String fieldName = excel.importName();
                        //如果注解中的importName(导入字段名)和key(excel标题)一致,则进行设值
                        if (StringUtils.isNotEmpty(fieldName) && key.equals(fieldName)) {
                            String fieldValue = entityMap.get(key);
                            field.setAccessible(true);
                            Object value = null;
                            if (!StringUtil.isBlank(fieldValue)) {
                                value = typeCast(fieldValue, fieldType, fieldName, String.valueOf(line));
                            }
                            field.set(object, value);
                            break;
                        }
                    }
                }
            }
            objectList.add(object);
            line++;
        }
        return objectList;
    }
 /**
     * 将上传的exel文件转成对应实体集合
     *
     * @param file 上传的导入文件
     * @param pojoClass 导出的数据的class
     * @return
     */
    public static List<Object> importExel(MultipartFile file, Class<?> pojoClass) throws Exception {
        //创建excel工作簿
        Workbook workbook = getWorkBook(file);
        int index = 0;
        //获取第一个sheet
        Sheet sheet = workbook.getSheetAt(0);
        //把每行转换成<标题名,值>的map集合
        List<Map<String, String>> mapList = new ArrayList<>();
        //获取标题行
        Row titleRow = sheet.getRow(0);
        //开始的单元格下标
        int startCell = titleRow.getFirstCellNum();
        //末尾的单元格下标
        int endCell = titleRow.getLastCellNum();
        //遍历excel行数
        for (Row row : sheet) {
            //取得该行行数
            int rowNum = row.getRowNum();
            //我这里有两行标题行,所以row要大于1,才能去取数据,而row.getCell(4) != null是为了判断该行数据是不是空的,excel定义个序号列,必填项,序号不为空才代表这行有数据,可以去读取
            if (rowNum > 1 && row.getCell(0) != null) {
                //map给个初始长度,因为大部分情况,作为开发,是知道到底要导入多少字段的
                Map<String, String> valueMap = new HashMap<>(60);
                for (int i = startCell; i < endCell; i++) {
                    //取得表头名
                    String title = titleRow.getCell(i).getStringCellValue();
                    //取得单元格数值
                    String value = Objects.nonNull(row.getCell(i)) ? getCellValue(row.getCell(i)) : null;
                    valueMap.put(title, value);
                }
                mapList.add(valueMap);
            }
        }
        //转化完后集合要是为空,说明没数值,给出提示
        if (CollectionUtils.isEmpty(mapList)) {
            throw new BusinessException("暂无数据可导入");
        }
        //最终返回要导入的实例集合
        return convertMapToEntity(mapList, pojoClass);
    }

使用方法:

  1. 注解定义在要导入的实体字段上,类似:

    2.直接调用上面列出的importExel(MultipartFile file, Class<?> pojoClass) 方法,传入对应参数就行

    3.将返回的Object集合转换成你要导入的实体集合,调用批量新增的接口

小建议:
这里可以创建一个导入结果类,类似:

然后将successImportList做插入数据库操作
errorImportList再加一个导入错误信息的字段,将导入的错误写到这个字段,然后调用导出方法,将errorImportList数据导出,方便用户查看原因。


转载:https://blog.csdn.net/weixin_38106322/article/details/101777220
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场