Springboot操作Poi進行Excel匯入

书画船妄月發表於2024-08-23




以上就是我所要匯入的Excel模板。

需求重點
經過這次需求的實現,發現這個需求最難的點 就在 模板模板模板!
模板中資料的關係,理清楚思路
模板中的資料,到底是多sheet還是說有合併單元進行匯入

實現感悟
不管是多個sheet、還是說合並單元格、最重要的思想都是說將Excel中的資料進行拼接為一條一條的List記錄進行新增資料。

    /**
     * 用於檢測是否是表頭行,此方法就是說程式碼再執行的時候找到一級這個欄位的名稱,然後知道獲取欄位從此開始
     *
     * @param row
     * @return
     */
    private boolean isHeaderRow(Row row) {
        Cell firstCell = row.getCell(1);
        return firstCell != null && "一級".equals(firstCell.getStringCellValue().trim());
    }
    /**
     *  
     * 上傳檔案,進行解析
     * @param file 檔案
     * @return
     */
    public Result<?> importExcels(MultipartFile file) {
        Map<Integer, List<Object>> sheetDataMap = new HashMap<>();
        try {
            // 獲取工作簿,解析整個Excel檔案
            Workbook workbook = new XSSFWorkbook(file.getInputStream());

            // 迴圈遍歷每個Sheet
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                Sheet sheet = workbook.getSheetAt(i);
                // 解析每個Sheet的資料
                List<Object> sheetData = parseSheet(sheet);
                // 將Sheet的資料儲存到Map中,以Sheet名稱為Key
                sheetDataMap.put(i, sheetData);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 處理資料
        processingData(sheetDataMap);
        return Result.ok(sheetDataMap);
    }
     /**
     * 解析給定的Sheet,並將資料儲存到List中
     *
     * @param sheet 要解析的Sheet
     * @return 包含每行資料的List
     */
    private List<Object> parseSheet(Sheet sheet) {
        List<Object> data = new ArrayList<>();

        boolean isHeaderFound = false;

        // 迴圈遍歷每一行資料
        for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
            Row row = sheet.getRow(rowIndex);
            if (row == null) continue; // 跳過空行

            // 檢查是否為標題行
            if (!isHeaderFound) {
                if (isHeaderRow(row)) {
                    isHeaderFound = true; // 找到標題行
                }
                continue; // 跳過標題行的處理
            }

            // 收集每列的資料
            List<Object> rowData = new ArrayList<>();
            for (int cellIndex = 1; cellIndex <= row.getLastCellNum(); cellIndex++) {
                Cell cell = row.getCell(cellIndex);
                if (cell != null && isMergedCell(sheet, row, cell)) {
                    rowData.add(getMergedCellValue(sheet, row, cell));
                } else if (cell != null && !cell.toString().trim().isEmpty()) {
                    rowData.add(cell.toString()); // 僅當單元格不為空且不為空字串時才新增
                }
            }
            if (!rowData.isEmpty()) {
                data.add(rowData); // 僅在行資料非空時才新增到資料列表中
            }
        }
        return data;
    }
    /**
     * 獲取合併單元格的值
     */
    private String getMergedCellValue(Sheet sheet, Row row, Cell cell) {
        int rowIndex = row.getRowNum();
        int colIndex = cell.getColumnIndex();
        for (int i = 1; i < sheet.getNumMergedRegions(); i++) {
            CellRangeAddress range = sheet.getMergedRegion(i);
            if (range.isInRange(rowIndex, colIndex)) {
                Row firstRow = sheet.getRow(range.getFirstRow());
                Cell firstCell = firstRow.getCell(range.getFirstColumn());
                return firstCell.toString().replace("\n", " ");
            }
        }
        return null;
    }
    /**
     * 判斷指定單元格是否是合併單元格
     */
    private boolean isMergedCell(Sheet sheet, Row row, Cell cell) {
        int rowIndex = row.getRowNum();
        int colIndex = cell.getColumnIndex();
        for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
            CellRangeAddress range = sheet.getMergedRegion(i);
            if (range.isInRange(rowIndex, colIndex)) {
                return true;
            }
        }
        return false;
    }
     /**
     * 對於讀取的Excel資料進行處理。
     *
     * @param data
     */
    private boolean processingData(Map<Integer, List<Object>> data) {
        boolean flag = false;
        try {
            // 遍歷每個層級的資料,從0開始,每個level對應一個sheet的資料
            for (int level = 0; level < data.size(); level++) {
                List<Object> levelList = data.get(level);
                if (levelList == null) {
                    continue; // 如果當前層級沒有資料,跳過
                }

                // 遍歷當前層級中的每一行資料
                for (Object o : levelList) {
                    List<String> rowData = (List<String>) o;

                    // 獲取父類名稱(如果是第一級,則父類名稱為null)
                    String parentName = level == 0 ? null : rowData.get(level - 1);
                    // 獲取當前的名稱
                    String currentName = rowData.get(level);

                    try {
                        String parentId = "0"; // 預設父類ID為"0",表示頂級目錄
                        if (level > 0 && parentName != null) {
                            // 如果不是第一級,透過父類名稱查詢父類ID
                            Zbk parentZbk = zbkMapper.selectOne(new LambdaQueryWrapper<Zbk>()
                                    .eq(Zbk::getName, parentName.replace("\n", "")) // 去除換行符後匹配名稱
                                    .eq(Zbk::getType, 1) // 型別為目錄
                                    .eq(Zbk::getDeleted, 0)); // 未被刪除

                            if (parentZbk != null) {
                                parentId = parentZbk.getId(); // 如果找到父類,獲取父類ID
                            } else {
                                log.warn("父類不存在: " + parentName);
                                continue;
                            }
                        }

                        // 查詢是否已經存在當前名稱的指標/目錄
                        Zbk existingZbk = zbkMapper.selectOne(new LambdaQueryWrapper<Zbk>()
                                .eq(Zbk::getName, currentName.replace("\n", "")) // 去除換行符後匹配名稱
                                .eq(Zbk::getType, level == data.size() - 1 ? 0 : 1) // 如果是最後一級,型別為指標,否則為目錄
                                .eq(Zbk::getDeleted, 0) // 未被刪除
                                .eq(Zbk::getPid, parentId)); // 父類ID匹配

                        // 如果當前名稱的指標/目錄不存在,插入新的記錄
                        if (existingZbk == null) {
                            Zbk zbk = new Zbk();
                            zbk.setName(currentName);
                            zbk.setDeleted(0); // 邏輯刪除標誌設為未刪除
                            zbk.setType(level == data.size() - 1 ? 0 : 1); // 設定型別,0為指標,1為目錄
                            zbk.setEvaluationObject("1382586667761336322");
                            zbk.setZbSourceType("build");
                            zbk.setDisable(false); // 未禁用
                            zbk.setPid(parentId); // 設定父類ID
                            zbkMapper.insert(zbk); // 插入新記錄

                            // 如果是最後一級(觀測點),處理觀測點的額外邏輯
                            if (level == data.size() - 1) {
                                flag = true;
                                // 獲取指標型別
                                String indicatorType = rowData.get(rowData.size() - 2);
                                // 獲取指標分值
                                String indicatorScore = rowData.get(rowData.size() - 1);
                                if (!StringUtils.isEmpty(indicatorType) && !StringUtils.isEmpty(indicatorScore)) {
                                    // 處理指標資料
                                    processingIndicators(indicatorType, indicatorScore, zbk.getId());
                                }
                            }
                        }
                    } catch (Exception e) {
                        log.error("處理資料時發生異常: ", e);
                        continue; // 如果發生異常,跳過當前迴圈
                    }
                }
            }
        } catch (Exception e) {
            log.error("處理資料時發生異常: ", e);
            e.printStackTrace();
        }
        return flag; // 返回處理結果的標誌
    }

相關文章