使用 Apache POI 实现 Excel 文件读写(导入 导出)操作的工具类

使用 Apache POI 实现 Excel 文件读写(导入 / 导出)操作的工具类

一、介绍

Apache POI 是 Apache 基金会提供的用于操作 Microsoft Office 文档的 Java 库,支持 Excel(.xls/.xlsx)、Word、PPT 等格式。本文实现的工具类基于 Apache POI,封装了 Excel 文件的导入(读取)和导出(写入)核心功能,兼容 Excel 97-2003(.xls)和 Excel 2007+(.xlsx)格式,适用于常见的 Excel 数据处理场景。

二、实现

1.依赖

xml 复制代码
<!-- Excel 2007+ (.xlsx) 核心依赖 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.5</version>
</dependency>
<!-- Excel 97-2003 (.xls) 支持 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>5.2.5</version>
</dependency>
<!-- 可选:处理公式单元格 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>4.1.2</version>
</dependency>
JDK 版本与 Apache POI 适配关系
Apache POI 版本 最低 JDK 版本 推荐 JDK 版本 备注
3.x 系列(如 3.17、3.19) JDK 6 JDK 6/7/8 兼容老项目,已停止维护
4.x 系列(如 4.1.2、4.2.2) JDK 8 JDK 8/11 主流稳定版本,兼容大部分场景
5.x 系列(如 5.2.0、5.2.5) JDK 11 JDK 11/17 最新特性支持,推荐 JDK 11+
5.2.3+ JDK 11 JDK 11/17/21 适配更高版本 JDK,支持模块化

三、实现

java 复制代码
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Excel 读写工具类,支持.xls(Excel 97-2003)和.xlsx(Excel 2007+)格式
 */
public class ExcelUtils {

    // ==================== 读取 Excel(导入) ====================
    /**
     * 读取 Excel 文件内容(文件路径)
     * @param filePath Excel 文件路径
     * @return 每行数据以 Map 存储(key=表头,value=单元格值)
     * @throws IOException IO 异常
     */
    public static List<Map<String, Object>> readExcel(String filePath) throws IOException {
        return readExcel(new FileInputStream(new File(filePath)));
    }

    /**
     * 读取 Excel 输入流内容
     * @param inputStream Excel 输入流
     * @return 解析后的数据列表
     * @throws IOException IO 异常
     */
    public static List<Map<String, Object>> readExcel(InputStream inputStream) throws IOException {
        List<Map<String, Object>> dataList = new ArrayList<>();
        Workbook workbook = null;

        try {
            // 自动识别 Excel 格式并创建 Workbook
            workbook = WorkbookFactory.create(inputStream);
            Sheet sheet = workbook.getSheetAt(0); // 读取第一个工作表
            if (sheet == null || sheet.getLastRowNum() < 0) {
                return dataList; // 空表格直接返回
            }

            // 获取表头行(第一行)
            Row headerRow = sheet.getRow(0);
            int headerCellCount = headerRow.getPhysicalNumberOfCells();

            // 遍历数据行(从第二行开始)
            for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
                Row dataRow = sheet.getRow(rowNum);
                if (dataRow == null) continue; // 跳过空行

                Map<String, Object> rowData = new HashMap<>();
                for (int cellNum = 0; cellNum < headerCellCount; cellNum++) {
                    Cell headerCell = headerRow.getCell(cellNum);
                    Cell dataCell = dataRow.getCell(cellNum);

                    String header = getCellValue(headerCell); // 表头名称
                    Object value = getCellValue(dataCell);    // 单元格值
                    rowData.put(header, value);
                }
                dataList.add(rowData);
            }
        } finally {
            // 关闭资源
            if (workbook != null) workbook.close();
            inputStream.close();
        }
        return dataList;
    }

    // ==================== 写入 Excel(导出) ====================
    /**
     * 导出 Excel 文件(文件路径)
     * @param dataList 导出数据(List<Map>,key=表头,value=单元格值)
     * @param headers 表头数组(与 dataList 的 key 对应)
     * @param filePath 输出文件路径(.xls 或.xlsx 结尾)
     * @throws IOException IO 异常
     */
    public static void writeExcel(List<Map<String, Object>> dataList, String[] headers, String filePath) throws IOException {
        boolean isXlsx = filePath.endsWith(".xlsx");
        try (FileOutputStream outputStream = new FileOutputStream(new File(filePath))) {
            writeExcel(dataList, headers, outputStream, isXlsx);
        }
    }

    /**
     * 导出 Excel 到输出流
     * @param dataList 导出数据
     * @param headers 表头数组
     * @param outputStream 输出流
     * @param isXlsx 是否为.xlsx 格式(true=.xlsx,false=.xls)
     * @throws IOException IO 异常
     */
    public static void writeExcel(List<Map<String, Object>> dataList, String[] headers,
                                 OutputStream outputStream, boolean isXlsx) throws IOException {
        // 创建 Workbook(根据格式选择 XSSF 或 HSSF)
        Workbook workbook = isXlsx ? new XSSFWorkbook() : new HSSFWorkbook();
        Sheet sheet = workbook.createSheet("Sheet1"); // 创建工作表

        // 1. 创建表头行
        Row headerRow = sheet.createRow(0);
        for (int i = 0; i < headers.length; i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers[i]);
            sheet.autoSizeColumn(i); // 自动调整列宽
        }

        // 2. 填充数据行
        for (int rowNum = 0; rowNum < dataList.size(); rowNum++) {
            Row dataRow = sheet.createRow(rowNum + 1);
            Map<String, Object> rowData = dataList.get(rowNum);

            for (int cellNum = 0; cellNum < headers.length; cellNum++) {
                String header = headers[cellNum];
                Object value = rowData.get(header);
                Cell cell = dataRow.createCell(cellNum);
                setCellValue(cell, value); // 设置单元格值
            }
        }

        // 写入输出流并关闭资源
        workbook.write(outputStream);
        workbook.close();
    }

    // ==================== 辅助方法 ====================
    /**
     * 获取单元格值(兼容不同数据类型)
     * @param cell 单元格对象
     * @return 单元格值(统一转为字符串)
     */
    private static String getCellValue(Cell cell) {
        if (cell == null) return "";

        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue().trim();
            case NUMERIC:
                // 处理日期格式
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getDateCellValue().toString();
                } else {
                    // 处理数字(避免科学计数法)
                    return String.valueOf((long) cell.getNumericCellValue());
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                // 计算公式结果
                return cell.getCellFormula() + "=" + cell.getNumericCellValue();
            default:
                return "";
        }
    }

    /**
     * 设置单元格值(兼容不同数据类型)
     * @param cell 单元格对象
     * @param value 待设置的值
     */
    private static void setCellValue(Cell cell, Object value) {
        if (value == null) {
            cell.setCellValue("");
            return;
        }

        if (value instanceof String) {
            cell.setCellValue((String) value);
        } else if (value instanceof Number) {
            cell.setCellValue(((Number) value).doubleValue());
        } else if (value instanceof Boolean) {
            cell.setCellValue((Boolean) value);
        } else if (value instanceof java.util.Date) {
            cell.setCellValue((java.util.Date) value);
        } else {
            cell.setCellValue(value.toString());
        }
    }
}
//WorkbookFactory.create() 是 Apache POI 4.x+ 提供的自动识别格式方法,简化了格式判断逻辑。

四、说明

  1. 核心

    • Workbook:Excel 文档的顶层对象,XSSFWorkbook 对应.xlsx,HSSFWorkbook 对应.xls。
    • Sheet:工作表对象,一个 Workbook 可包含多个 Sheet。
    • Row:行对象,通过 Sheet 创建或获取。
    • Cell:单元格对象,通过 Row 创建或获取,需处理不同数据类型(字符串、数字、日期等)。
  2. 思路

    • 读取 Excel :先解析表头行,再逐行读取数据,将每行数据封装为 Map<表头, 值>
    • 写入 Excel:先创建表头,再遍历数据列表填充行数据,自动适配.xls/.xlsx 格式。
    • 辅助方法getCellValue 统一解析单元格值,setCellValue 支持多种数据类型写入。
  3. 补充

  • 空值处理:读取时跳过空行,写入时空值转为空字符串,避免单元格类型异常。
  • 日期处理 :通过 DateUtil.isCellDateFormatted 识别日期格式单元格,避免数字形式的日期错误。
  • 资源关闭 :使用 try-finallytry-with-resources 确保 Workbook 和流资源关闭,防止内存泄漏。

五、扩展

多 Sheet 支持:修改工具类,支持读取指定 Sheet 或遍历所有 Sheet。

java 复制代码
// 读取指定名称的 Sheet
public static List<Map<String, Object>> readExcel(String filePath, String sheetName) throws IOException {
    Workbook workbook = WorkbookFactory.create(new FileInputStream(filePath));
    Sheet sheet = workbook.getSheet(sheetName);
    // 后续逻辑同单 Sheet 读取...
}

样式定制:添加单元格样式(字体、颜色、对齐方式等),示例:

java 复制代码
// 创建样式
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBold(true);
style.setFont(font);
headerRow.getCell(0).setCellStyle(style); // 表头加粗

大数据量优化 :对于超大 Excel(百万行级),使用 SXSSFWorkbook(.xlsx)或 HSSFWorkbook 分批写入,避免内存溢出:

java 复制代码
// 超大文件导出(SXSSF 支持)
Workbook workbook = new SXSSFWorkbook(100); // 内存中保留 100 行,超出后写入磁盘

六、其他

Excel 格式区别
.xls(Excel 97-2003 格式)
  • 核心特性:基于二进制格式(BIFF 格式)存储,是早期 Excel 版本的默认格式。
  • 容量限制 :最大支持 65536 行256 列,无法处理超大规模数据。
  • 兼容性:兼容所有 Excel 版本,但在新版 Excel 中打开时可能提示 "兼容模式"。
  • 文件大小:相同数据下文件体积更大,且易受二进制文件损坏影响。
  • 功能限制:不支持新版 Excel 特性(如条件格式、切片器、XML 数据映射等)。
.xlsx(Excel 2007+ 格式)
  • 核心特性:基于 Office Open XML(OOXML)标准,采用 XML 压缩格式存储。
  • 容量限制 :最大支持 1048576 行16384 列,可处理海量数据。
  • 兼容性:需 Excel 2007 及以上版本支持,低版本需安装兼容包。
  • 文件大小:因 XML 压缩机制,文件体积更小,且损坏后更易修复。
  • 功能支持:支持新版 Excel 所有特性(如公式扩展、样式定制、数据透视表增强等)。

单元格类型:Excel 单元格支持 6 种类型:STRING(字符串)、NUMERIC(数字 / 日期)、BOOLEAN(布尔)、FORMULA(公式)、BLANK(空)、ERROR(错误),需针对性解析。

单元格类型 初始读取的数据类型 示例场景
NUMERIC 数值型(double/long) 整数、小数、日期(特殊)
STRING 字符串(String) 文本、手动输入的数字
BOOLEAN 布尔值(boolean) TRUE/FALSE
FORMULA 公式计算结果(类型动态) =A1+B1 的结果
BLANK 空字符串("") 空单元格
相关推荐
咕咕嘎嘎10241 小时前
C/C++内存对齐
java·c语言·c++
认真敲代码的小火龙1 小时前
【JAVA项目】基于JAVA的图书管理系统
java·开发语言·课程设计
西岭千秋雪_1 小时前
MySQL日志梳理(存储引擎层)
java·数据库·分布式·mysql·oracle
2301_797312261 小时前
学习Java22天
java·开发语言
老华带你飞2 小时前
英语学习|基于Java英语学习系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·后端·学习
qq_479875432 小时前
C++ 模板元编程
java·开发语言·c++
codingPower2 小时前
Java EasyExcel创建复杂表格的完整指南:WriteTable
java·开发语言
爱学习的小可爱卢2 小时前
编程语言30年:从Java到Rust的进化史
java·开发语言·rust
一 乐2 小时前
校园社区系统|基于java+vue的校园悬赏任务平台系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot