1. 概述

本教程将演示如何使用 Apache POI、JExcel 和 Fastexcel 这三大主流库来操作 Excel 表格。这些库可以动态读取、写入和修改 Excel 文件内容,为 Java 应用程序集成 Microsoft Excel 提供了高效解决方案。

2. Maven 依赖

首先需要在 pom.xml 中添加以下依赖:

<dependency> 
  <groupId>org.apache.poi</groupId>
  <artifactId>poi</artifactId> 
  <version>5.2.5</version> 
</dependency> 
<dependency> 
  <groupId>org.apache.poi</groupId>
  <artifactId>poi-ooxml</artifactId> 
  <version>5.2.5</version> 
</dependency>
<dependency>
  <groupId>org.jxls</groupId>
  <artifactId>jxls-jexcel</artifactId>
  <version>1.0.9</version>
</dependency>
<dependency>
    <groupId>org.dhatim</groupId>
    <artifactId>fastexcel-reader</artifactId>
    <version>0.17.0</version>
</dependency>
<dependency>
    <groupId>org.dhatim</groupId>
    <artifactId>fastexcel</artifactId>
    <version>0.17.0</version>
</dependency>

最新版本可从 Maven Central 下载:

3. Apache POI

Apache POI 同时支持 .xls.xlsx 格式,是 Java 操作 Excel 文件中最复杂的库。它提供了:

  • Workbook 接口:表示 Excel 文件
  • Sheet/Row/Cell 接口:表示 Excel 元素
  • 针对不同格式的实现类

文件格式对应关系: | 格式 | Workbook 实现 | Sheet 实现 | Row 实现 | Cell 实现 | |--------|--------------|-----------|----------|-----------| | .xlsx | XSSFWorkbook | XSSFSheet | XSSFRow | XSSFCell | | .xls | HSSFWorkbook | HSSFSheet | HSSFRow | HSSFCell |

3.1. 读取 Excel

创建方法读取 .xlsx 文件的首个工作表内容。读取方式需根据单元格类型动态选择

FileInputStream file = new FileInputStream(new File(fileLocation));
Workbook workbook = new XSSFWorkbook(file);

获取首工作表并遍历行:

Sheet sheet = workbook.getSheetAt(0);

Map<Integer, List<String>> data = new HashMap<>();
int i = 0;
for (Row row : sheet) {
    data.put(i, new ArrayList<String>());
    for (Cell cell : row) {
        switch (cell.getCellType()) {
            case STRING: ... break;
            case NUMERIC: ... break;
            case BOOLEAN: ... break;
            case FORMULA: ... break;
            default: data.get(new Integer(i)).add(" ");
        }
    }
    i++;
}

⚠️ 不同数据类型的读取方法

  • STRING 类型
    data.get(new Integer(i)).add(cell.getRichStringCellValue().getString());
    
  • NUMERIC 类型(需区分日期和数字):
    if (DateUtil.isCellDateFormatted(cell)) {
        data.get(i).add(cell.getDateCellValue() + "");
    } else {
        data.get(i).add(cell.getNumericCellValue() + "");
    }
    
  • BOOLEAN 类型
    data.get(i).add(cell.getBooleanCellValue() + "");
    
  • FORMULA 类型
    data.get(i).add(cell.getCellFormula() + "");
    

3.2. 写入 Excel

Apache POI 使用相同接口写入文件,样式支持比 JExcel 更强大。创建方法写入人员列表到 "Persons" 工作表:

创建带样式的表头

Workbook workbook = new XSSFWorkbook();

Sheet sheet = workbook.createSheet("Persons");
sheet.setColumnWidth(0, 6000);
sheet.setColumnWidth(1, 4000);

Row header = sheet.createRow(0);

CellStyle headerStyle = workbook.createCellStyle();
headerStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

XSSFFont font = ((XSSFWorkbook) workbook).createFont();
font.setFontName("Arial");
font.setFontHeightInPoints((short) 16);
font.setBold(true);
headerStyle.setFont(font);

Cell headerCell = header.createCell(0);
headerCell.setCellValue("Name");
headerCell.setCellStyle(headerStyle);

headerCell = header.createCell(1);
headerCell.setCellValue("Age");
headerCell.setCellStyle(headerStyle);

写入数据行

CellStyle style = workbook.createCellStyle();
style.setWrapText(true);

Row row = sheet.createRow(2);
Cell cell = row.createCell(0);
cell.setCellValue("John Smith");
cell.setCellStyle(style);

cell = row.createCell(1);
cell.setCellValue(20);
cell.setCellStyle(style);

保存文件

File currDir = new File(".");
String path = currDir.getAbsolutePath();
String fileLocation = path.substring(0, path.length() - 1) + "temp.xlsx";

FileOutputStream outputStream = new FileOutputStream(fileLocation);
workbook.write(outputStream);
workbook.close();

JUnit 测试验证

public class ExcelIntegrationTest {

    private ExcelPOIHelper excelPOIHelper;
    private static String FILE_NAME = "temp.xlsx";
    private String fileLocation;

    @Before
    public void generateExcelFile() throws IOException {
        File currDir = new File(".");
        String path = currDir.getAbsolutePath();
        fileLocation = path.substring(0, path.length() - 1) + FILE_NAME;

        excelPOIHelper = new ExcelPOIHelper();
        excelPOIHelper.writeExcel();
    }

    @Test
    public void whenParsingPOIExcelFile_thenCorrect() throws IOException {
        Map<Integer, List<String>> data
          = excelPOIHelper.readExcel(fileLocation);

        assertEquals("Name", data.get(0).get(0));
        assertEquals("Age", data.get(0).get(1));

        assertEquals("John Smith", data.get(1).get(0));
        assertEquals("20", data.get(1).get(1));
    }
}

4. JExcel

JExcel 是轻量级库,优势是比 Apache POI 更简单易用,但**仅支持 .xls(1997-2003)格式**。❌ 目前不支持 .xlsx 文件

4.1. 读取 Excel

核心类结构:

  • Workbook:表示整个工作簿
  • Sheet:表示单个工作表
  • Cell:表示单元格

读取方法示例:

public class JExcelHelper {

    public Map<Integer, List<String>> readJExcel(String fileLocation) 
      throws IOException, BiffException {
 
        Map<Integer, List<String>> data = new HashMap<>();

        Workbook workbook = Workbook.getWorkbook(new File(fileLocation));
        Sheet sheet = workbook.getSheet(0);
        int rows = sheet.getRows();
        int columns = sheet.getColumns();

        for (int i = 0; i < rows; i++) {
            data.put(i, new ArrayList<String>());
            for (int j = 0; j < columns; j++) {
                data.get(i)
                  .add(sheet.getCell(j, i)
                  .getContents());
            }
        }
        return data;
    }
}

4.2. 写入 Excel

写入相关类:

  • WritableWorkbook:可写工作簿
  • WritableSheet:可写工作表
  • WritableCell:可写单元格(含子类:Label/DateTime/Number/Boolean/Blank/Formula

创建工作簿

File currDir = new File(".");
String path = currDir.getAbsolutePath();
String fileLocation = path.substring(0, path.length() - 1) + "temp.xls";

WritableWorkbook workbook = Workbook.createWorkbook(new File(fileLocation));

创建表头

WritableSheet sheet = workbook.createSheet("Sheet 1", 0);

WritableCellFormat headerFormat = new WritableCellFormat();
WritableFont font
  = new WritableFont(WritableFont.ARIAL, 16, WritableFont.BOLD);
headerFormat.setFont(font);
headerFormat.setBackground(Colour.LIGHT_BLUE);
headerFormat.setWrap(true);

Label headerLabel = new Label(0, 0, "Name", headerFormat);
sheet.setColumnView(0, 60);
sheet.addCell(headerLabel);

headerLabel = new Label(1, 0, "Age", headerFormat);
sheet.setColumnView(0, 40);
sheet.addCell(headerLabel);

写入数据

WritableCellFormat cellFormat = new WritableCellFormat();
cellFormat.setWrap(true);

Label cellLabel = new Label(0, 2, "John Smith", cellFormat);
sheet.addCell(cellLabel);
Number cellNumber = new Number(1, 2, 20, cellFormat);
sheet.addCell(cellNumber);

保存并关闭

workbook.write();
workbook.close();

⚠️ 踩坑提醒:务必调用 write()close(),否则文件可能损坏!

5. Fastexcel

Fastexcel 是轻量级库,功能有限但内存占用远低于 Apache POI。其 CompletableFuture 多线程支持使其成为处理大型简单文件的理想选择。目前仅支持基础样式,不支持图表。

5.1. 读取 Excel

读取方法示例:

public class FastexcelHelper {

    public Map<Integer, List<String>> readExcel(String fileLocation) throws IOException {
        Map<Integer, List<String>> data = new HashMap<>();

        try (FileInputStream file = new FileInputStream(fileLocation); ReadableWorkbook wb = new ReadableWorkbook(file)) {
            Sheet sheet = wb.getFirstSheet();
            try (Stream<Row> rows = sheet.openStream()) {
                rows.forEach(r -> {
                    data.put(r.getRowNum(), new ArrayList<>());

                    for (Cell cell : r) {
                        data.get(r.getRowNum()).add(cell.getRawValue());
                    }
                });
            }
        }

        return data;
    }
}

读取单元格的多种方式

  • cell.getRawValue():直接获取字符串值
  • 根据类型调用:
    row.getCellAsBoolean(0);    // 布尔值
    row.getCellAsDate(1);       // 日期
    row.getCellAsString(2);     // 字符串
    row.getCellAsNumber(3);     // 数字
    

5.2. 写入 Excel

Fastexcel 的读写接口完全不同,写入示例:

public void writeExcel() throws IOException {
    File currDir = new File(".");
    String path = currDir.getAbsolutePath();
    String fileLocation = path.substring(0, path.length() - 1) + "fastexcel.xlsx";

    try (OutputStream os = Files.newOutputStream(Paths.get(fileLocation)); Workbook wb = new Workbook(os, "MyApplication", "1.0")) {
        Worksheet ws = wb.newWorksheet("Sheet 1");
        ws.width(0, 25);
        ws.width(1, 15);
        
        ws.range(0, 0, 0, 1).style().fontName("Arial").fontSize(16).bold().fillColor("3366FF").set();
        ws.value(0, 0, "Name");
        ws.value(0, 1, "Age");
        
        ws.range(2, 0, 2, 1).style().wrapText(true).set();
        ws.value(2, 0, "John Smith");
        ws.value(2, 1, 20L);
    }
}

测试验证

@Test
public void whenParsingExcelFile_thenCorrect() throws IOException {
    Map<Integer, List<String>> data = fastexcelHelper.readExcel(fileLocation);

    assertEquals("Name", data.get(1).get(0));
    assertEquals("Age", data.get(1).get(1));

    assertEquals("John Smith", data.get(3).get(0));
    assertEquals("20", data.get(3).get(1));
}

⚠️ 索引差异注意

  • fastexcel-readerRow 使用 1-based 行号(rowNum
  • fastexcelWorksheet.value() 使用 0-based 索引

6. 结论

本文展示了如何使用 Apache POI、JExcel 和 Fastexcel 在 Java 中读写 Excel 文件。选择库时需权衡:

特性 Apache POI JExcel Fastexcel
支持格式 .xls / .xlsx .xls .xlsx
功能丰富度 ✅ 极高(含图表支持) ❌ 基础 ❌ 基础
内存占用 ❌ 高 ✅ 低 ✅ 极低
学习曲线 ❌ 陡峭 ✅ 平缓 ✅ 简单
多线程支持 ❌ 有限 ❌ 无 ✅ 优秀

💡 选型建议

  • 复杂功能(图表/公式)→ Apache POI
  • 老项目 .xls 文件 → JExcel
  • 大文件高性能处理 → Fastexcel

完整代码示例见 GitHub 仓库


原始标题:Working with Microsoft Excel in Java