杰瑞科技汇

Java如何用POI导出Excel?

目录

  1. 准备工作:引入 POI 依赖
  2. 基础入门:导出一个简单的 .xls 文件 (HSSFWorkbook)
  3. 进阶:导出 .xlsx 文件 (XSSFWorkbook)
  4. 常用功能详解
    • 创建复杂表头(合并单元格)
    • 设置单元格样式(字体、颜色、边框、对齐方式)
    • 设置列宽和行高
    • 写入日期和数字格式
  5. 数据量大时的优化:SXSSFWorkbook (流式 API)
  6. 完整示例:带样式的复杂 Excel 导出
  7. 总结与注意事项

准备工作:引入 POI 依赖

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

Java如何用POI导出Excel?-图1
(图片来源网络,侵删)

重要提示: POI 的不同模块需要分开引入。

<dependencies>
    <!-- 核心 POI 库,用于处理 .xls 和 .xlsx -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.2.4</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.4</version>
    </dependency>
    <!-- 用于处理 .xlsx 格式的 OOXML 架构 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-lite</artifactId>
        <version>5.2.4</version>
    </dependency>
</dependencies>

依赖版本说明:

  • poi: 处理旧版 Excel 格式(.xls)。
  • poi-ooxml: 处理新版 Office Open XML 格式(.xlsx),它依赖于 poi
  • poi-ooxml-lite: 一个轻量级的 poi-ooxml,如果你不需要 XSLF(PowerPoint)等模块,可以使用它来减少最终 jar 包的大小。

基础入门:导出一个简单的 .xls 文件 (HSSFWorkbook)

.xls 是 Excel 97-2003 的格式,使用 HSSFWorkbook 类,由于行数和列数限制(最多 65536 行,256 列),现在已不推荐用于生产环境,但它是理解 POI 工作原理的好起点。

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class SimpleXlsExport {
    public static void main(String[] args) {
        // 1. 创建一个 HSSFWorkbook 对象,代表一个 Excel 文件
        Workbook workbook = new HSSFWorkbook();
        // 2. 创建一个 Sheet,名为 "Sheet1"
        Sheet sheet = workbook.createSheet("用户数据");
        // 3. 创建表头行(第 0 行)
        Row headerRow = sheet.createRow(0);
        headerRow.createCell(0).setCellValue("ID");
        headerRow.createCell(1).setCellValue("姓名");
        headerRow.createCell(2).setCellValue("年龄");
        // 4. 创建数据行
        Row dataRow1 = sheet.createRow(1);
        dataRow1.createCell(0).setCellValue(1);
        dataRow1.createCell(1).setCellValue("张三");
        dataRow1.createCell(2).setCellValue(25);
        Row dataRow2 = sheet.createRow(2);
        dataRow2.createCell(0).setCellValue(2);
        dataRow2.createCell(1).setCellValue("李四");
        dataRow2.createCell(2).setCellValue(30);
        // 5. 写入文件
        try (FileOutputStream fileOut = new FileOutputStream("D:/temp/users.xls")) {
            workbook.write(fileOut);
            System.out.println("users.xls 文件生成成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 6. 关闭 workbook 对象,释放资源
            try {
                workbook.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

进阶:导出 .xlsx 文件 (XSSFWorkbook)

.xlsx 是目前主流的 Excel 格式,它基于 XML,没有行数和列数的严格限制(受限于内存),我们使用 XSSFWorkbook 类。

Java如何用POI导出Excel?-图2
(图片来源网络,侵删)

代码与 .xls 版本几乎完全相同,只需替换 HSSFWorkbookXSSFWorkbook,并修改输出文件名。

import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class SimpleXlsxExport {
    public static void main(String[] args) {
        // 1. 创建一个 XSSFWorkbook 对象
        Workbook workbook = new XSSFWorkbook();
        // 2. 创建 Sheet
        Sheet sheet = workbook.createSheet("产品列表");
        // 3. 创建表头
        Row headerRow = sheet.createRow(0);
        headerRow.createCell(0).setCellValue("产品ID");
        headerRow.createCell(1).setCellValue("产品名称");
        headerRow.createCell(2).setCellValue("价格");
        // 4. 创建数据
        Row dataRow1 = sheet.createRow(1);
        dataRow1.createCell(0).setCellValue("P001");
        dataRow1.createCell(1).setCellValue("笔记本电脑");
        dataRow1.createCell(2).setCellValue(5999.99);
        // 5. 写入文件
        try (FileOutputStream fileOut = new FileOutputStream("D:/temp/products.xlsx")) {
            workbook.write(fileOut);
            System.out.println("products.xlsx 文件生成成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 6. 关闭 workbook
            try {
                workbook.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

常用功能详解

1 创建复杂表头(合并单元格)

使用 sheet.addMergedRegion() 方法来合并单元格。

// 假设在 sheet 对象上操作
// 合并第一行的第0列到第2列,创建一个大的表头
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 2));
Row bigHeaderRow = sheet.createRow(0);
bigHeaderRow.createCell(0).setCellValue("用户信息总表");
// 合并第二行的第0列和第1列,创建二级表头
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 1));
Row subHeaderRow = sheet.createRow(1);
subHeaderRow.createCell(0).setCellValue("基本信息");
subHeaderRow.createCell(2).setCellValue("年龄");

2 设置单元格样式

样式是 POI 中比较重要的部分,通过 CellStyle 类来设置。

import org.apache.poi.ss.usermodel.*;
// 1. 创建一个样式对象
CellStyle style = workbook.createCellStyle();
// 2. 设置字体
Font font = workbook.createFont();
font.setFontName("Arial");
font.setFontHeightInPoints((short) 12); // 字号
font.setBold(true); // 加粗
style.setFont(font);
// 3. 设置对齐方式
style.setAlignment(HorizontalAlignment.CENTER); // 水平居中
style.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
// 4. 设置边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
// 5. 应用样式到单元格
Cell cell = headerRow.createCell(0);
cell.setCellValue("ID");
cell.setCellStyle(style);

3 设置列宽和行高

// 设置第一列的宽度,宽度是以字符个数为单位的
sheet.setColumnWidth(0, 15 * 256); // 15个字符宽
// 设置第二列的宽度为自适应内容
sheet.autoSizeColumn(1);
// 设置第三行的高度,高度是以 1/20 个点为单位的
sheet.getRow(2).setHeight((short) (30 * 20));

4 写入日期和数字格式

使用 DataFormat 来设置单元格的数据格式。

Java如何用POI导出Excel?-图3
(图片来源网络,侵删)
import java.util.Date;
// 创建一个日期格式
CellStyle dateStyle = workbook.createCellStyle();
CreationHelper createHelper = workbook.getCreationHelper();
dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy-MM-dd"));
// 创建一个数字格式(保留两位小数)
CellStyle numberStyle = workbook.createCellStyle();
numberStyle.setDataFormat(createHelper.createDataFormat().getFormat("#.00"));
// 应用样式
Row row = sheet.createRow(3);
Cell dateCell = row.createCell(0);
dateCell.setCellValue(new Date());
dateCell.setCellStyle(dateStyle);
Cell numberCell = row.createCell(1);
numberCell.setCellValue(123.456);
numberCell.setCellStyle(numberStyle);

数据量大时的优化:SXSSFWorkbook

当导出数据量非常大(例如几十万行)时,使用 XSSFWorkbook 会导致内存溢出,因为 XSSFWorkbook 会将所有数据都加载到内存中。

SXSSFWorkbook (Streaming Usermodel API) 是 POI 提供的解决方案,它采用“滑动窗口”的机制,只保留一部分数据在内存中,其余数据临时写入磁盘,从而大大减少内存消耗。

import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class LargeDataExport {
    public static void main(String[] args) {
        // 1. 创建 SXSSFWorkbook,参数 100 表示内存中保留的行数
        // 当超过这个数量时,早先的数据会被写入临时文件
        Workbook workbook = new SXSSFWorkbook(100);
        // 2. 创建 Sheet
        Sheet sheet = workbook.createSheet("大数据报表");
        // 3. 模拟写入 10 万行数据
        for (int i = 0; i < 100000; i++) {
            Row row = sheet.createRow(i);
            row.createCell(0).setCellValue("ID_" + i);
            row.createCell(1).setCellValue("用户名称_" + i);
            // 在控制台打印进度
            if (i % 10000 == 0) {
                System.out.println("已写入 " + i + " 行数据...");
            }
        }
        // 4. 写入文件
        try (FileOutputStream fileOut = new FileOutputStream("D:/temp/large_data.xlsx")) {
            workbook.write(fileOut);
            System.out.println("large_data.xlsx 文件生成成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5. 清理临时文件(非常重要!)
            ((SXSSFWorkbook) workbook).dispose();
            try {
                workbook.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

注意:

  • SXSSFWorkbook 只支持 .xlsx 格式。
  • 必须调用 dispose() 方法来删除在磁盘上生成的临时文件。
  • SXSSFWorkbook 的一些功能(如 sheet.setColumnWidth)在数据量极大时可能会有性能问题,或者需要提前设置。

完整示例:带样式的复杂 Excel 导出

下面是一个综合了前面所有知识点的完整示例,导出一个包含合并单元格、多种样式、日期和数字格式的复杂 Excel 文件。

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
public class ComplexExcelExport {
    public static void main(String[] args) {
        // 1. 创建 Workbook
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("销售报表");
        // 2. 创建样式
        // 标题样式
       CellStyle titleStyle = createTitleStyle(workbook);
        // 表头样式
       CellStyle headerStyle = createHeaderStyle(workbook);
        // 日期样式
       CellStyle dateStyle = createDateStyle(workbook);
        // 金额样式
       CellStyle currencyStyle = createCurrencyStyle(workbook);
        // 3. 写入数据
        writeData(sheet, titleStyle, headerStyle, dateStyle, currencyStyle);
        // 4. 调整列宽
        sheet.autoSizeColumn(0);
        sheet.autoSizeColumn(1);
        sheet.setColumnWidth(2, 20 * 256); // 固定日期列宽
        sheet.autoSizeColumn(3);
        // 5. 写入文件
        try (FileOutputStream fileOut = new FileOutputStream("D:/temp/sales_report.xlsx")) {
            workbook.write(fileOut);
            System.out.println("sales_report.xlsx 文件生成成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                workbook.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    private static void writeData(Sheet sheet, CellStyle titleStyle, CellStyle headerStyle, CellStyle dateStyle, CellStyle currencyStyle) {
        // 合并单元格创建大标题
        sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 3));
        Row titleRow = sheet.createRow(0);
        titleRow.createCell(0).setCellValue("2025年第四季度销售报告");
        titleRow.getCell(0).setCellStyle(titleStyle);
        // 创建二级表头
        sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 1));
        Row subHeaderRow = sheet.createRow(1);
        subHeaderRow.createCell(0).setCellValue("订单信息");
        subHeaderRow.getCell(0).setCellStyle(headerStyle);
        subHeaderRow.createCell(2).setCellValue("日期");
        subHeaderRow.getCell(2).setCellStyle(headerStyle);
        subHeaderRow.createCell(3).setCellValue("金额");
        subHeaderRow.getCell(3).setCellStyle(headerStyle);
        // 写入数据行
        for (int i = 2; i < 12; i++) {
            Row dataRow = sheet.createRow(i);
            dataRow.createCell(0).setCellValue("ORD-" + (1000 + i));
            dataRow.createCell(1).setCellValue("商品名称 " + i);
            dataRow.createCell(2).setCellValue(new Date());
            dataRow.getCell(2).setCellStyle(dateStyle);
            dataRow.createCell(3).setCellValue(1000.00 * i);
            dataRow.getCell(3).setCellStyle(currencyStyle);
        }
    }
    private static CellStyle createTitleStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setFontHeightInPoints((short) 16);
        font.setBold(true);
        style.setFont(font);
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        return style;
    }
    private static CellStyle createHeaderStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setBold(true);
        style.setFont(font);
        style.setAlignment(HorizontalAlignment.CENTER);
        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);
        return style;
    }
    private static CellStyle createDateStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        style.setDataFormat(workbook.createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss"));
        return style;
    }
    private static CellStyle createCurrencyStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        style.setDataFormat(workbook.createDataFormat().getFormat("#,##0.00"));
        style.setAlignment(HorizontalAlignment.RIGHT);
        return style;
    }
}

总结与注意事项

  1. 格式选择:优先使用 XSSFWorkbook (.xlsx) 格式,因为它更现代、功能更强、没有行数限制,仅在需要兼容旧版 Excel (.xls) 时使用 HSSFWorkbook
  2. 内存管理:对于大数据量导出,必须使用 SXSSFWorkbook,并记得在最后调用 dispose() 方法清理临时文件。
  3. 样式复用:创建样式对象(CellStyle, Font)时,尽量在循环外创建,然后在循环中重复使用,而不是为每个单元格都创建一个新样式,这样可以显著提高性能并减少内存占用。
  4. 关闭资源:使用 try-with-resources 语句(如 try (FileOutputStream...))来确保文件流被正确关闭,或者在 finally 块中手动关闭,防止资源泄漏。
  5. 中文乱码问题:如果导出的 Excel 文件用 Excel 打开时出现乱码,通常是因为代码文件的编码或 POI 内部处理问题,确保你的 Java 源文件是 UTF-8 编码,POI 版本较新(5.x 版本对 Unicode 支持很好),在 Web 应用中,设置正确的 Content-TypeContent-Disposition 响应头也很重要。
分享:
扫描分享到社交APP
上一篇
下一篇