杰瑞科技汇

Java读写Excel用POI怎么实现?

Apache POI 是一个强大的 Java 库,专门用于操作 Microsoft Office 格式的文件,包括 Excel(.xls.xlsx)。

Java读写Excel用POI怎么实现?-图1
(图片来源网络,侵删)

目录

  1. 准备工作:添加 POI 依赖
  2. 核心概念:XSSFWorkbook vs HSSFWorkbook
  3. 写入 Excel 文件
    • 写入 .xlsx (Excel 2007+)
    • 写入 .xls (Excel 97-2003)
    • 设置单元格样式(字体、颜色、边框等)
    • 调整列宽
    • 合并单元格
  4. 读取 Excel 文件
    • 读取 .xlsx (Excel 2007+)
    • 读取 .xls (Excel 97-2003)
    • 获取单元格数据
    • 处理不同类型的数据(字符串、数字、日期等)
  5. 完整示例代码
  6. 最佳实践与注意事项

准备工作:添加 POI 依赖

您需要在您的项目中添加 Apache POI 的依赖,如果您使用 Maven,请在 pom.xml 文件中添加以下依赖:

<!-- 对于较新版本的 POI,建议使用这个方式 -->
<dependencies>
    <!-- 核心 POI 库 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.2.5</version> <!-- 请使用最新稳定版 -->
    </dependency>
    <!-- 用于处理 .xlsx 格式 (OOXML) -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.5</version>
    </dependency>
    <!-- 用于处理 XML,是 poi-ooxml 的依赖 -->
    <dependency>
        <groupId>org.apache.xmlbeans</groupId>
        <artifactId>xmlbeans</artifactId>
        <version>5.1.1</version>
    </dependency>
    <!-- 用于处理 .xlsx 中的图片等 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-scratchpad</artifactId>
        <version>5.2.5</version>
    </dependency>
</dependencies>

注意.xlsx 格式是现代 Excel 的标准,推荐优先使用,它需要 poipoi-ooxml 两个依赖,而 .xls 格式只需要 poi 依赖即可。


核心概念:XSSFWorkbook vs HSSFWorkbook

这是使用 POI 时最重要的区别:

特性 HSSFWorkbook XSSFWorkbook
文件格式 .xls (BIFF 格式) .xlsx (OOXML 格式)
Excel 版本 Excel 97-2003 Excel 2007 及更高版本
扩展性 最大支持 65536 行,256 最大支持 1,048,576 行,16,384
内存占用 相对较低 相对较高,但 API 更现代
API 设计 旧版 API 新版 API,与旧版不兼容

简单来说

Java读写Excel用POI怎么实现?-图2
(图片来源网络,侵删)
  • 如果你要处理新版本的 Excel 文件(.xlsx),使用 XSSFWorkbook
  • 如果你要处理旧版本的 Excel 文件(.xls),使用 HSSFWorkbook

写入 Excel 文件

1 写入 .xlsx (Excel 2007+)

这是最推荐的方式。

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class WriteExcelXLSX {
    public static void main(String[] args) {
        // 1. 创建一个新的 Workbook 对象 (代表整个 Excel 文件)
        Workbook workbook = new XSSFWorkbook();
        try {
            // 2. 创建一个 Sheet (工作表)
            Sheet sheet = workbook.createSheet("员工信息");
            // 3. 创建 Row (行) 和 Cell (单元格)
            // 创建表头
            Row headerRow = sheet.createRow(0);
            headerRow.createCell(0).setCellValue("ID");
            headerRow.createCell(1).setCellValue("姓名");
            headerRow.createCell(2).setCellValue("年龄");
            headerRow.createCell(3).setCellValue("入职日期");
            // 创建数据行
            Row dataRow1 = sheet.createRow(1);
            dataRow1.createCell(0).setCellValue(1);
            dataRow1.createCell(1).setCellValue("张三");
            dataRow1.createCell(2).setCellValue(30);
            dataRow1.createCell(3).setCellValue(setDateCellValue(workbook, "2025-05-15")); // 设置日期格式
            Row dataRow2 = sheet.createRow(2);
            dataRow2.createCell(0).setCellValue(2);
            dataRow2.createCell(1).setCellValue("李四");
            dataRow2.createCell(2).setCellValue(28);
            dataRow2.createCell(3).setCellValue(setDateCellValue(workbook, "2025-08-20"));
            // 4. 调整列宽以适应内容
            sheet.autoSizeColumn(0);
            sheet.autoSizeColumn(1);
            sheet.autoSizeColumn(2);
            sheet.autoSizeColumn(3);
            // 5. 将 Workbook 写入到文件输出流
            try (FileOutputStream fileOut = new FileOutputStream("employees.xlsx")) {
                workbook.write(fileOut);
                System.out.println("employees.xlsx 文件已成功创建!");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 6. 关闭 Workbook,释放资源
            try {
                if (workbook != null) {
                    workbook.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 创建一个日期格式的单元格
     */
    private static Cell setDateCellValue(Workbook workbook, String dateStr) {
        CreationHelper createHelper = workbook.getCreationHelper();
        CellStyle dateStyle = workbook.createCellStyle();
        dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy-mm-dd"));
        Cell cell = null; // 假设这个 cell 已经被创建
        cell.setCellStyle(dateStyle);
        // 注意:这里需要单独设置 cell 的值,因为 setCellStyle 不会设置值
        // 实际使用时,应该先创建 cell,再设置样式,再设置值
        // 为简化示例,这里返回一个已设置好样式的 Cell 对象供外部设置值
        return cell;
    }
}

2 设置单元格样式(字体、颜色、边框)

// ... (在写入文件的代码中)
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
// 设置字体为粗体
font.setBold(true);
style.setFont(font);
// 设置背景色
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 设置边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
// 应用样式到单元格
headerRow.getCell(0).setCellStyle(style);

3 调整列宽和合并单元格

// ... (在写入文件的代码中)
// 调整第一列的宽度为 15 个字符宽度
sheet.setColumnWidth(0, 15 * 256); // 宽度单位是 1/256 个字符宽度
// 合并单元格 (参数:起始行, 结束行, 起始列, 结束列)
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 3)); // 合并第一行的第0列到第3列

读取 Excel 文件

读取文件的流程与写入相反:先打开文件流,创建 Workbook,然后循环遍历 SheetRowCell

1 读取 .xlsx (Excel 2007+)

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
public class ReadExcelXLSX {
    public static void main(String[] args) {
        String filePath = "employees.xlsx";
        try (FileInputStream fis = new FileInputStream(filePath);
             Workbook workbook = new XSSFWorkbook(fis)) {
            // 1. 获取第一个 Sheet
            Sheet sheet = workbook.getSheetAt(0); // 或 getSheet("员工信息")
            // 2. 遍历每一行 (从0开始,0是表头)
            for (Row row : sheet) {
                // 跳过表头行
                if (row.getRowNum() == 0) {
                    continue;
                }
                // 3. 遍历每一个单元格
                for (Cell cell : row) {
                    // 4. 根据单元格类型获取值
                    switch (cell.getCellType()) {
                        case STRING:
                            System.out.print(cell.getStringCellValue() + "\t");
                            break;
                        case NUMERIC:
                            // 检查是数字还是日期
                            if (DateUtil.isCellDateFormatted(cell)) {
                                System.out.print(cell.getDateCellValue() + "\t");
                            } else {
                                System.out.print(cell.getNumericCellValue() + "\t");
                            }
                            break;
                        case BOOLEAN:
                            System.out.print(cell.getBooleanCellValue() + "\t");
                            break;
                        case FORMULA:
                            // 如果单元格包含公式,可以获取公式的计算结果
                            System.out.print(cell.getCellFormula() + "\t");
                            break;
                        case BLANK:
                            System.out.print("[空]\t");
                            break;
                        default:
                            System.out.print("[未知类型]\t");
                    }
                }
                System.out.println(); // 换行
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2 读取 .xls (Excel 97-2003)

代码几乎完全一样,只需要将 XSSFWorkbook 替换为 HSSFWorkbook,文件流改为 .xls 文件即可。

// ...
try (FileInputStream fis = new FileInputStream("employees.xls");
     Workbook workbook = new HSSFWorkbook(fis)) {
    // ... 其余代码完全相同
}
// ...

完整示例代码

下面是一个结合了读取和写入的完整示例,它从一个模板 .xlsx 文件读取数据,处理后写入一个新的文件。

Java读写Excel用POI怎么实现?-图3
(图片来源网络,侵删)
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class PoiReadWriteExample {
    public static void main(String[] args) {
        String inputFile = "template.xlsx"; // 假设这个文件存在
        String outputFile = "output.xlsx";
        try (FileInputStream fis = new FileInputStream(inputFile);
             Workbook workbook = new XSSFWorkbook(fis);
             FileOutputStream fos = new FileOutputStream(outputFile)) {
            // --- 1. 读取操作 ---
            Sheet readSheet = workbook.getSheet("数据源");
            System.out.println("--- 读取模板文件 ---");
            for (Row row : readSheet) {
                for (Cell cell : row) {
                    System.out.print(getCellValueAsString(cell) + "\t");
                }
                System.out.println();
            }
            // --- 2. 写入操作 ---
            Sheet writeSheet = workbook.createSheet("处理结果");
            // 创建标题行
            Row titleRow = writeSheet.createRow(0);
            titleRow.createCell(0).setCellValue("处理后的ID");
            titleRow.createCell(1).setCellValue("处理后的名称");
            // 模拟数据处理
            int id = 100;
            String name = "新用户";
            Row dataRow = writeSheet.createRow(1);
            dataRow.createCell(0).setCellValue(id);
            dataRow.createCell(1).setCellValue(name);
            // 设置样式
            CellStyle headerStyle = workbook.createCellStyle();
            Font headerFont = workbook.createFont();
            headerFont.setBold(true);
            headerStyle.setFont(headerFont);
            titleRow.getCell(0).setCellStyle(headerStyle);
            titleRow.getCell(1).setCellStyle(headerStyle);
            writeSheet.autoSizeColumn(0);
            writeSheet.autoSizeColumn(1);
            // 3. 写入文件
            workbook.write(fos);
            System.out.println("\n--- 数据已成功写入 " + outputFile + " ---");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 将单元格值转换为字符串
     */
    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();
                } else {
                    return String.valueOf(cell.getNumericCellValue());
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                return cell.getCellFormula();
            case BLANK:
                return "";
            default:
                return "";
        }
    }
}

最佳实践与注意事项

  1. 使用 try-with-resourcesWorkbook, FileInputStream, FileOutputStream 等都实现了 AutoCloseable 接口,使用 try-with-resources 语句可以确保它们在使用后被自动关闭,避免资源泄漏。

    try (Workbook workbook = new XSSFWorkbook()) {
        // ... 操作 workbook
    } // workbook 在这里会自动关闭
  2. 选择正确的 Workbook 类型:始终根据文件格式(.xls.xlsx)选择 HSSFWorkbookXSSFWorkbook,对于新项目,强烈推荐只使用 .xlsxXSSFWorkbook

  3. 处理大数据量(SXSSF:如果需要处理非常大的 Excel 文件(包含数万行数据),使用 XSSFWorkbook 会导致内存溢出,这时应该使用 SXSSFWorkbook(流 API)。

    • SXSSFWorkbookXSSFWorkbook 的一个流式实现,它只在内存中保留一定数量的行(默认100行),其余的行会临时写入磁盘。
    • 示例:Workbook workbook = new SXSSFWorkbook(100); // 内存中保留100行
  4. 日期处理:直接使用 cell.setCellValue(new Date())setCellValue("yyyy-mm-dd") 可能不会显示为你期望的格式,必须先创建一个 CellStyle 并设置日期格式,然后将这个 CellStyle 应用到单元格上。

  5. 单元格类型判断:在读取单元格时,必须使用 cell.getCellType() 来判断单元格的数据类型,然后调用相应的 getXXXCellValue() 方法,不要假设一个单元格里的数字就是 IntegerDouble,它可能是公式计算的结果,也可能是字符串形式的数字,POI 提供了 DataFormatter 类来统一格式化单元格的显示值。

    DataFormatter dataFormatter = new DataFormatter();
    String cellValue = dataFormatter.formatCellValue(cell); // 获取单元格显示的值
分享:
扫描分享到社交APP
上一篇
下一篇