首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

POI SAX 事件驅動解析大型 Excel(.xlsx):解決空儲存格導致列錯位難題

     編輯:Java 綜合教學

背景與挑戰

專案需解析 20MB+、萬行級的 Excel 2007 (.xlsx) 檔案。POI 預設的 XSSFWorkbook 模式會將整份檔案載入記憶體,極易 OOM。改用 SAX 事件驅動模式(XSSFReader + ContentHandler)流式讀取,記憶體佔用極低。但 .xlsx 內部採用共享字串表與稀疏儲存:空儲存格在 XML 中完全不存在節點,直接導致列解析時列號跳躍、資料錯位。

核心解決方案:列號追蹤 + 字串緩衝補齊

關鍵邏輯在 endElement("v") 與 endElement("row") 中:

public class MyXSSFSheetHandler extends DefaultHandler {
    // 關鍵狀態
    private int lastColumnNumber = -1;   // 上一個實際讀到的列索引
    private int thisColumn = -1;         // 當前儲存格列索引(由 r="A3" 解析)
    private StringBuffer rowString = new StringBuffer(); // 整行 CSV 字串緩衝

    @Override
    public void startElement(String uri, String localName, String name, Attributes attrs) {
        if ("c".equals(name)) { // 儲存格開始
            String r = attrs.getValue("r"); // 如 "A3", "C5"
            thisColumn = nameToColumn(r.substring(0, firstDigitIndex(r))); // 字母轉列號 0-based
            // ... 型別/樣式解析略 ...
        }
    }

    @Override
    public void endElement(String uri, String localName, String name) {
        if ("v".equals(name)) { // 儲存格值結束
            String cellValue = parseCellValue(); // 依型別解析 thisStr

            // === 核心補齊邏輯 ===
            if (lastColumnNumber == -1) lastColumnNumber = 0;
            for (int i = lastColumnNumber; i < thisColumn; i++) {
                rowString.append(','); // 補齊中間空儲存格
            }
            rowString.append(cellValue); // 追加當前值
            lastColumnNumber = thisColumn;
        } else if ("row".equals(name)) { // 整行結束
            // 補齊行尾空儲存格(若有 minColumns 限制)
            if (minColumns > 0) {
                for (int i = lastColumnNumber; i < minColumns; i++)
                    rowString.append(',');
            }
            // 以逗號分割得到完整欄位陣列/Map
            Map<Integer, String> rowMap = rowReader.getRowMapByString(rowString.toString());
            // 批次收集、落庫邏輯...
            rowString.setLength(0); // 重置緩衝
            lastColumnNumber = -1;
        }
    }
}

關鍵點解析

  1. nameToColumn():將 "A"→0, "Z"→25, "AA"→26,支援多字母列名。
  2. rowString 緩衝:避免 List.add() 丟失空值問題,CSV 字串天然保留空欄位順序。
  3. 逆序補逗號:for (i=last; i<this; i++) 精確補齊 last+1 ... this-1 之間的空列。
  4. minColumns 參數:強制每行輸出固定列數,防止行尾空值丟失。

效能實測數據

檔案大小資料行數耗時記憶體峰值
20 MB10,000+~2 分鐘< 100 MB
40 MB20,000+~4 分鐘< 150 MB

進階優化建議

  • 批次寫入:每 1000 行組裝 List<Map> 入庫,減少資料庫交互。
  • 型別快取:StylesTable、SharedStringsTable 單例複用,避免重複解析。
  • 並行處理:多 Sheet 可開多執行緒並行,單 Sheet 順序讀取難以並行化。
  • 替代方案:EasyExcel (Alibaba) 內建 SAX 封裝,支援註解映射、自動空值補齊,生產環境首選。

參考資源

「SAX 解析 Excel 的本質是『流式掃描 XML』,空儲存格不存在節點是 XML 結構決定的,唯有『列號追蹤 + 字串緩衝』才能還原真實表格結構。」

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved