动态列表的excel导入、导出功能(二)

项目场景:

最近做到一个业务需求是,要做一个物品管理页面的excel模板导出,导出的excel信息填充后做导入使用


问题描述:

接着上次做出的’半动态导出‘功能后,开始编写相应的导入功能,一开始思路没有分析好,往传统的模板导入走去了,想着只要字段属性足够就能配合接收各种模板的导入(这个也是以往我做导入功能的局限:往往一种业务的导入功能就得创建相应字段的对象),等我成功接收数据后,发现值是收到了,没有办法取出做判断;
后面选择用gridExcel,配合动态生成的模板 实现无实体类的导入功能:

1. 导入jar包

    <!-- 无实体类导入-->
        <!-- https://mvnrepository.com/artifact/com.github.liuhuagui/gridexcel -->
        <dependency>
            <groupId>com.github.liuhuagui</groupId>
            <artifactId>gridexcel</artifactId>
            <version>2.3</version>
        </dependency>

2. 导入工具类设计:

  public static Workbook getWorkBook(MultipartFile file) {
    
    
        //创建Workbook工作薄对象,表示整个excel
        Workbook workbook = null;
        try {
    
    
            //获取excel文件的io流
            InputStream is = file.getInputStream();

            workbook = WorkbookFactory.create(is);
        } catch (InvalidFormatException e) {
    
    
            e.printStackTrace();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } catch (org.apache.poi.openxml4j.exceptions.InvalidFormatException e) {
    
    
            e.printStackTrace();
        }
        return workbook;
    }

    public static String getCellValue(Cell cell) {
    
    
        String cellValue = "";
        if (cell == null) {
    
    
            return cellValue;
        }
        //把数字当成String来读,避免出现1读成1.0的情况
        if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
    
    
            cell.setCellType(Cell.CELL_TYPE_STRING);
        }
        //判断数据的类型
        switch (cell.getCellType()) {
    
    
            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: //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;
    }
    
    /**
     * 读入excel文件,解析后返回
     *
     * @param file excel 文件
     * @return list中的每个数组是每一行excel,list中的第一条数据是excel的表头,其余之后的是excel中的数据
     */
    public static List<String[]> readExcel(MultipartFile file) throws IOException {
    
    

        //获得Workbook工作薄对象
        Workbook workbook = getWorkBook(file);
        //创建返回对象,把每行中的值作为一个数组,所有行作为一个集合返回
        List<String[]> list = new ArrayList<String[]>();
        if (workbook != null) {
    
    
            //只读取第一个sheet页
            for (int sheetNum = 0; sheetNum < 1; sheetNum++) {
    
    
                //获得当前sheet工作表
                Sheet sheet = workbook.getSheetAt(sheetNum);
                if (sheet == null) {
    
    
                    continue;
                }
                //获得当前sheet的开始行
                int firstRowNum = sheet.getFirstRowNum();
                //获得当前sheet的结束行
                int lastRowNum = sheet.getLastRowNum();
                //循环所有行
                for (int rowNum = firstRowNum; rowNum <= lastRowNum; rowNum++) {
    
    
                    //获得当前行
                    Row row = sheet.getRow(rowNum);
                    if (row == null) {
    
    
                        continue;
                    }
                    //获得当前行的开始列
                    int firstCellNum = row.getFirstCellNum();
                    //获得当前行的列数
                    int lastCellNum;
                    if (rowNum == 0) {
    
    
                        lastCellNum = row.getPhysicalNumberOfCells();
                    } else {
    
    
                        lastCellNum = list.get(0).length;
                    }
                    String[] cells = new String[lastCellNum];  //需要更改  是数组长度  “row.getPhysicalNumberOfCells()”
                    //循环当前行
                    for (int cellNum = firstCellNum; cellNum < lastCellNum; cellNum++) {
    
     //需要更改循环长度 “lastCellNum”
                        Cell cell = row.getCell(cellNum);
                        cells[cellNum] = getCellValue(cell);
                    }
                    list.add(cells);
                }
            }
        }
        return list;
    }


由于动态模板的设计,接收的数据因为模板的原因会接收到空值,这里做下数据处理方便后续模板校验


    /**
     * excel接收数据加工
     **/
    public static List<List<String>> dealData(List<String[]> data){
    
    
        //数组转为集合
        List<List<String>> datas = new ArrayList<>();
        for (String[] arr:data) {
    
    
            List<String> list1= Arrays.asList(arr);
            List<String> arrList = new ArrayList<String>(list1);
            datas.add(arrList);
        }
        //根据列头去空
        for(List<String> list : datas){
    
    
            for (int i = 0; i < list.size(); i++) {
    
    
                String str = list.get(i).trim();
                int length = str.length();
                if(str == "" ||str ==null ||length == 0) {
    
    
                    list.remove(i);
                    i=i-1;
                }
            }
        }
        return datas;
    }

    /**
    * 对比集合是否相同(无重复项)
    **/
    public static boolean isEquals(List<String> list1, List<String> list2){
    
    
        if (list1 == null && list2 == null) {
    
    
            return true;
        }else if(list1 == null || list2 == null) {
    
    
            return false;
        }else if(list1.size() != list2.size()) {
    
    
            return false;
        }
        Set<String> set1 = new TreeSet<>(list1);
        Set<String> set2 = new TreeSet<>(list2);
        return set1.equals(set2);
    }

在这里插入图片描述

3. Controller层设计:

  /**
    * 文件导入(无实体类导入)
    **/
    @PostMapping("/office/goods/import")
    @ResponseBody
    public ResponseResult upload(@RequestParam("file") MultipartFile multipartFile,String orgCode,String typeCode, String personCode,String flag) throws Exception {
    
    
        try{
    
    
            if (multipartFile != null && multipartFile.getSize() > 0) {
    
    
                //返回的第一条数据是表头信息
                //data 解析出来的excel数据, list第一条数据是表头数据,第二条数据开始为     表头下的内容
                List<String[]> data = GhPOIUtils.readExcel(multipartFile);

                //过滤空数数据(数组转集合,循环去空)
                List<List<String>> datas = GhPOIUtils.dealData(data);

                //校验模板
                //step1 获取表列名
                OfficeTypeContentVo officeTypeContent = new OfficeTypeContentVo();
                officeTypeContent.setTypeCode(typeCode);
                List<OfficeTypeContentVo> moderList = officeTypeService.getTypeContent(officeTypeContent);
                //step2 模板格式对比
                if(datas.get(0).size()-1== moderList.size()){
    
    
                    List<String>titleList = datas.get(0);
                    List<String>tempList = new ArrayList<>();
                    for(OfficeTypeContentVo vo:moderList){
    
    
                        tempList.add(vo.getColumnName());
                    }

                    titleList.remove(0);
                    Boolean res =GhPOIUtils.isEquals(titleList,tempList);
                    if(!res){
    
    
                        log.error("模板错误:导入列名不匹配!");
                        return WebUtil.success("模板错误:导入列名不匹配!!");
                    }
                }else{
    
    
                    log.error("模板错误:导入列数不匹配!");
                    return WebUtil.success("模板错误:导入列数不匹配!");
                }

                //信息对象生成
                List<OfficeGoodsVo>addList = new ArrayList<>();
                //......                //数据导入
                boolean addresult = true;
                for(OfficeGoodsVo vo: addList){
    
    
                        addresult = officeGoodsService.addGoods(vo);
                        if(!addresult){
    
    
                            throw new BusinessException("文件导入失败!");
                        }
                }

                return WebUtil.success("文件导入成功!");
            }
         return WebUtil.success("空文件!");
    } catch (Exception e) {
    
    
         return WebUtil.success("文件导入操作异常!");
    }
}

参考资料:

GridExcel API

总结:

这次的动态导入导出功能的实现,过程中也走了不少弯路,但也让我进一步了解了各式excel工具类以及他们的差异,后续的代码优化想到也会慢慢更上。加油,每天多跨出一步!

猜你喜欢

转载自blog.csdn.net/weixin_50927151/article/details/121017344