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

Java 解析 Excel 文件完全指南:Apache POI 實現 .xls 與 .xlsx 雙格式支持

     編輯:Java 相關知識

前言

Apache POI 是 Java 生態中處理 Microsoft Office 文檔的事實標準庫。本文提供基於 POI 4.0+ 的完整解析方案,覆蓋舊版 .xls (HSSF) 與新版 .xlsx (XSSF) 雙格式,包含多類型單元格處理、大文件流式讀取、異常防禦等工程化實踐。

Maven 依賴

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

統一解析入口:工廠模式自適應格式

public interface ExcelParser {
    void parse(InputStream is, SheetHandler handler) throws IOException;
}

public class ExcelParserFactory {
    public static ExcelParser createParser(String fileName) {
        if (fileName.endsWith(".xlsx")) {
            return new XlsxParser();
        } else if (fileName.endsWith(".xls")) {
            return new XlsParser();
        }
        throw new IllegalArgumentException("不支持的文件格式: " + fileName);
    }
}

.xlsx 格式解析 (XSSF - XML 基礎)

public class XlsxParser implements ExcelParser {
    @Override
    public void parse(InputStream is, SheetHandler handler) throws IOException {
        try (XSSFWorkbook wb = new XSSFWorkbook(is)) {
            for (int i = 0; i < wb.getNumberOfSheets(); i++) {
                XSSFSheet sheet = wb.getSheetAt(i);
                handler.onSheetStart(sheet.getSheetName());
                parseSheet(sheet, handler);
                handler.onSheetEnd();
            }
        }
    }

    private void parseSheet(XSSFSheet sheet, SheetHandler handler) {
        for (Row row : sheet) { // 迭代器模式,自動跳過空行
            handler.onRowStart(row.getRowNum());
            for (Cell cell : row) {
                handler.onCell(cell.getColumnIndex(), getCellValue(cell));
            }
            handler.onRowEnd();
        }
    }

    private Object getCellValue(Cell cell) {
        switch (cell.getCellType()) {
            case STRING: return cell.getStringCellValue();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue(); // 日期類型特殊處理
                }
                return cell.getNumericCellValue();
            case BOOLEAN: return cell.getBooleanCellValue();
            case FORMULA: return cell.getCellFormula(); // 或評估值: cell.getNumericCellValue()
            case BLANK: return null;
            case ERROR: return cell.getErrorCellValue();
            default: return null;
        }
    }
}

.xls 格式解析 (HSSF - 二進制結構)

public class XlsParser implements ExcelParser {
    @Override
    public void parse(InputStream is, SheetHandler handler) throws IOException {
        try (POIFSFileSystem fs = new POIFSFileSystem(is);
             HSSFWorkbook wb = new HSSFWorkbook(fs)) {
            
            for (int i = 0; i < wb.getNumberOfSheets(); i++) {
                HSSFSheet sheet = wb.getSheetAt(i);
                handler.onSheetStart(sheet.getSheetName());
                parseSheet(sheet, handler);
                handler.onSheetEnd();
            }
        }
    }
    // parseSheet/getCellValue 邏輯與 XSSF 完全一致,得益於 SS 統一接口
}

關鍵工程化增強

1. 大文件流式讀取 (SXSSF / XSSF SAX)


// 內存友好: 僅保留最近 100 行在內存,其餘刷盤
try (SXSSFWorkbook wb = new SXSSFWorkbook(100)) {
    // 寫入大量數據...
    wb.dispose(); // 臨時文件清理
}

// 只讀流式: XSSFReader + SAX 事件驅動 (最低內存)
OPCPackage pkg = OPCPackage.open(file);
XSSFReader reader = new XSSFReader(pkg);
XMLReader parser = XMLReaderFactory.createXMLReader();
parser.setContentHandler(new SheetHandler(sheetIndex));
parser.parse(new InputSource(reader.getSheetsData().next()));

2. 日期/數字格式化處理

// 單元格樣式數據格式解析
DataFormatter formatter = new DataFormatter(Locale.CHINA);
String formattedValue = formatter.formatCellValue(cell);
// 自動處理: 日期→"2024-01-15"、貨幣→"¥1,234.56"、百分比→"12.34%"

3. 公式評估

FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
CellValue cellValue = evaluator.evaluate(cell);
switch (cellValue.getCellType()) {
    case NUMERIC: return cellValue.getNumberValue();
    case STRING: return cellValue.getStringValue();
    case BOOLEAN: return cellValue.getBooleanValue();
    case ERROR: return ErrorEval.getText(cellValue.getErrorValue());
}

4. 異常防禦與資源管理

  • 所有 Workbook/Stream 使用 try-with-resources
  • 捕獲 EncryptedDocumentException (加密文件)、OfficeXmlFileException (損壞文件)
  • 限制最大行/列數,防止惡意文件 OOM

回調處理器接口設計

public interface SheetHandler {
    void onSheetStart(String sheetName);
    void onSheetEnd();
    void onRowStart(int rowIndex);
    void onRowEnd();
    void onCell(int colIndex, Object value);
    void onError(Exception e); // 統一異常處理
}

完整使用示例

public class ExcelImportService {
    public void importData(MultipartFile file) throws IOException {
        ExcelParser parser = ExcelParserFactory.createParser(file.getOriginalFilename());
        
        parser.parse(file.getInputStream(), new SheetHandler() {
            private List<Map<String, Object>> batch = new ArrayList<>();
            private String[] headers;

            @Override
            public void onRowStart(int rowIndex) {
                if (rowIndex == 0) return; // 標題行單獨處理
            }

            @Override
            public void onCell(int colIndex, Object value) {
                if (rowIndex == 0) {
                    headers[colIndex] = String.valueOf(value);
                } else {
                    rowData.put(headers[colIndex], value);
                }
            }

            @Override
            public void onRowEnd() {
                if (rowIndex > 0) {
                    batch.add(rowData);
                    if (batch.size() >= 500) {
                        saveBatch(batch);
                        batch.clear();
                    }
                }
            }
        });
    }
}

常見坑點避雷

問題原因解決
數字讀成科學計數法默認 getNumericCellValue() 返回 double用 DataFormatter 或 DecimalFormat 格式化
日期讀成數字Excel 日期存儲為數字 (1900 起始天數)DateUtil.isCellDateFormatted() 判斷 + getDateCellValue()
大文件 OOMXSSF 全量載入 DOM 樹SXSSF (寫) / XSSFReader+SAX (讀)
公式結果為 null未評估公式FormulaEvaluator.evaluate()
.xls 讀取失敗用 XSSF 讀 HSSF 文件工廠模式自適應/POIFSFileSystem 識別

總結

Apache POI 提供了強大的 Excel 解析能力,關鍵在於:統一 SS 接口屏蔽格式差異、流式 API 解決大文件內存、DataFormatter 處理顯示格式、FormulaEvaluator 計算公式、工廠模式自適應雙格式。掌握這些核心模式,即可構建健壯、高性能的 Excel 導入導出系統。

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