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

Java 操作 Excel 實戰:Apache POI HSSF/XSSF 完整教程(讀寫/樣式/大數據/SXSSF)

     編輯:Java 相關知識

Apache POI 是 Java 操作 Excel 事實標準,支持 .xls (HSSF) 與 .xlsx (XSSF/SXSSF)。本文系統講解環境搭建、核心模型、讀寫基礎、樣式設計、公式計算、大數據流式處理(SXSSF)、常見坑點,並提供生產級工具類封裝。

一、依賴配置 (POI 5.3.0 / Java 11+)

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>5.3.0</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.3.0</version>
</dependency>
<!-- .xlsx 大數據流式寫入需 SXSSF -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-full</artifactId>
    <version>5.3.0</version>
</dependency>

二、核心對象模型對比

格式工作簿工作表行單元格行數上限內存模式
.xlsHSSFWorkbookHSSFSheetHSSFRowHSSFCell65,536 (216)全量 DOM
.xlsxXSSFWorkbookXSSFSheetXSSFRowXSSFCell1,048,576 (220)全量 DOM
.xlsx (流式)SXSSFWorkbookSXSSFSheetSXSSFRowSXSSFCell無上限滑動窗口 (默認 100 行)

三、統一讀取工具類 (支持 .xls/.xlsx 自動識別)

package com.util;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class ExcelReader {

    public static List> readAll(File file) throws IOException {
        try (InputStream is = new FileInputStream(file)) {
            return readAll(is, file.getName());
        }
    }

    public static List> readAll(InputStream is, String fileName) throws IOException {
        Workbook wb = createWorkbook(is, fileName);
        try {
            List> result = new ArrayList<>();
            for (int si = 0; si < wb.getNumberOfSheets(); si++) {
                Sheet sheet = wb.getSheetAt(si);
                for (Row row : sheet) {
                    List rowData = new ArrayList<>();
                    for (Cell cell : row) {
                        rowData.add(getCellValueAsString(cell));
                    }
                    if (!rowData.isEmpty()) result.add(rowData);
                }
            }
            return result;
        } finally {
            wb.close();
        }
    }

    private static Workbook createWorkbook(InputStream is, String fileName) throws IOException {
        String lower = fileName.toLowerCase();
        if (lower.endsWith(".xlsx")) return new XSSFWorkbook(is);
        if (lower.endsWith(".xls")) return new HSSFWorkbook(is);
        throw new IllegalArgumentException("不支持的格式:" + fileName);
    }

    private static String getCellValueAsString(Cell cell) {
        if (cell == null) return "";
        switch (cell.getCellType()) {
            case STRING: return cell.getStringCellValue();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue().toString();
                }
                // 避免科學計數法,去尾零
                double val = cell.getNumericCellValue();
                return (val == (long) val) ? String.valueOf((long) val) : String.valueOf(val);
            case BOOLEAN: return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                try {
                    return getCellValueAsString(cell.getCachedFormulaResultTypeEnum() != null 
                        ? cell : null); // 簡化:實際需評估公式
                } catch (Exception e) {
                    return cell.getCellFormula();
                }
            case BLANK: return "";
            case ERROR: return "#ERROR";
            default: return "";
        }
    }
}

四、寫入與樣式設計最佳實踐

樣式復用原則:Workbook 級創建,全局共享

try (XSSFWorkbook wb = new XSSFWorkbook()) {
    Sheet sheet = wb.createSheet("報表");

    // 1. 創建字體
    Font headerFont = wb.createFont();
    headerFont.setBold(true);
    headerFont.setFontHeightInPoints((short) 12);
    headerFont.setColor(IndexedColors.WHITE.getIndex());

    Font bodyFont = wb.createFont();
    bodyFont.setFontHeightInPoints((short) 11);

    // 2. 創建單元格樣式
    CellStyle headerStyle = wb.createCellStyle();
    headerStyle.setFont(headerFont);
    headerStyle.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex());
    headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
    headerStyle.setAlignment(HorizontalAlignment.CENTER);
    headerStyle.setBorderBottom(BorderStyle.THIN);
    headerStyle.setBorderTop(BorderStyle.THIN);
    headerStyle.setBorderLeft(BorderStyle.THIN);
    headerStyle.setBorderRight(BorderStyle.THIN);

    CellStyle bodyStyle = wb.createCellStyle();
    bodyStyle.setFont(bodyFont);
    bodyStyle.setAlignment(HorizontalAlignment.LEFT);
    bodyStyle.setBorderBottom(BorderStyle.THIN);
    bodyStyle.setBorderLeft(BorderStyle.THIN);
    bodyStyle.setBorderRight(BorderStyle.THIN);

    CellStyle numberStyle = wb.createCellStyle();
    numberStyle.cloneStyleFrom(bodyStyle);
    numberStyle.setDataFormat(wb.createDataFormat().getFormat("#,##0.00"));

    // 3. 寫入表頭
    Row headerRow = sheet.createRow(0);
    String[] headers = {"ID", "姓名", "金額", "日期"};
    for (int i = 0; i < headers.length; i++) {
        Cell cell = headerRow.createCell(i);
        cell.setCellValue(headers[i]);
        cell.setCellStyle(headerStyle);
    }

    // 4. 寫入數據
    for (int r = 0; r < 100; r++) {
        Row row = sheet.createRow(r + 1);
        Cell c0 = row.createCell(0); c0.setCellValue(r); c0.setCellStyle(bodyStyle);
        Cell c1 = row.createCell(1); c1.setCellValue("用戶" + r); c1.setCellStyle(bodyStyle);
        Cell c2 = row.createCell(2); c2.setCellValue(r * 100.5); c2.setCellStyle(numberStyle);
        Cell c3 = row.createCell(3); c3.setCellValue(new Date()); c3.setCellStyle(bodyStyle);
    }

    // 5. 自動列寬 (慎用大數據)
    for (int i = 0; i < headers.length; i++) {
        sheet.autoSizeColumn(i);
    }

    try (FileOutputStream fos = new FileOutputStream("report.xlsx")) {
        wb.write(fos);
    }
}

五、大數據導出:SXSSF 流式寫入 (關鍵技能)

// SXSSFWorkbook 包裝 XSSFWorkbook,滑動窗口默認 100 行
try (SXSSFWorkbook wb = new SXSSFWorkbook(1000)) { // 內存保留 1000 行
    Sheet sheet = wb.createSheet("大數據導出");
    // 樣式創建同 XSSF (SXSSF 繼承自 XSSF)

    for (int r = 0; r < 1_000_000; r++) {
        Row row = sheet.createRow(r);
        row.createCell(0).setCellValue(r);
        row.createCell(1).setCellValue("數據" + r);
        // 寫入即刷新到磁盤,內存只保留最近 1000 行
    }

    // 注意:SXSSF 不支持 autoSizeColumn (需預估寬度)
    sheet.setColumnWidth(0, 12 * 256);
    sheet.setColumnWidth(1, 30 * 256);

    // 臨時文件會寫入磁盤,關閉時自動清理
    try (FileOutputStream fos = new FileOutputStream("big_export.xlsx")) {
        wb.write(fos);
    }
    wb.dispose(); // 顯式清理臨時文件 (try-with-resources 會自動調用)
}

SXSSF 限制與注意

  • ❌ 不支持 autoSizeColumn、shiftRows、getRow 讀取已刷新行
  • ❌ 不支持合併單元格跨越窗口邊界
  • ✅ 支持樣式、公式、圖片、超鏈接、數據驗證
  • 臨時文件默認寫入 java.io.tmpdir,大導出需確保磁盤空間

六、公式計算與評估

try (XSSFWorkbook wb = new XSSFWorkbook(new FileInputStream("formula.xlsx"))) {
    Sheet sheet = wb.getSheetAt(0);
    FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();

    for (Row row : sheet) {
        for (Cell cell : row) {
            if (cell.getCellType() == CellType.FORMULA) {
                CellValue cellValue = evaluator.evaluate(cell);
                switch (cellValue.getCellType()) {
                    case NUMERIC: System.out.println(cellValue.getNumberValue()); break;
                    case STRING: System.out.println(cellValue.getStringValue()); break;
                    case BOOLEAN: System.out.println(cellValue.getBooleanValue()); break;
                    case ERROR: System.out.println(FormulaError.forInt(cellValue.getErrorValue()));
                }
            }
        }
    }
    // 寫回評估結果 (可選)
    evaluator.evaluateAll();
    try (FileOutputStream fos = new FileOutputStream("evaluated.xlsx")) {
        wb.write(fos);
    }
}

七、常見坑點避雷表

問題原因解決方案
數字顯示為科學計數法 (1.23E7)默認 General 格式設置 DataFormat 為 0 或 #,##0
日期讀出為數字 (44927)Excel 日期本質是數字用 DateUtil.isCellDateFormatted(cell) 判斷並轉換
大文件導出 OOMXSSF 全量內存改用 SXSSF 流式寫入
SXSSF 寫入後文件為空/損壞未調用 wb.write() 或 dispose()確保 try-with-resources 正確關閉
.xls 65536 行限制BIFF8 格式限制強制用 .xlsx + XSSF/SXSSF
合併單元格邊框消失POI 合併區域邊框需單獨設置用 RegionUtil.setBorderXXX() 設置合併區域邊框
中文亂碼字體不支持設置字體 font.setFontName("微軟雅黑"),確保服務器有字體

八、替代方案:EasyExcel (阿里開源)

若專案需極簡 API、極致性能、注解驅動,推薦 EasyExcel:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.0.5</version>
</dependency>

// 讀取
EasyExcel.read(file, DemoData.class, new DemoListener()).sheet().doRead();

// 寫入
EasyExcel.write(file, DemoData.class).sheet("模板").doWrite(dataList);

EasyExcel 內部基於 SAX 解析 + SXSSF,無需手動管理模型、樣式、內存,適合 90% 業務導入導出場景。


總結:Apache POI 功能最全、控制力最強,掌握 SXSSF 流式寫入是大數據導出必修課。日常業務推薦 EasyExcel 提升開發效率;複雜報表、精細樣式、模板填充、公式計算場景,POI 仍是不可替代的底層利器。統一工具類、樣式復用、異步處理、臨時文件清理,是生產級落地關鍵。

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