杰瑞科技汇

Java POI如何高效写入Excel数据?

目录

  1. 环境搭建

    添加 Maven/Gradle 依赖

    Java POI如何高效写入Excel数据?-图1
    (图片来源网络,侵删)
  2. 核心概念
    • Workbook (工作簿)
    • Sheet (工作表)
    • Row (行)
    • Cell (单元格)
  3. 完整示例:创建并写入 Excel
    • 创建 .xls (Excel 97-2003) 格式
    • 创建 .xlsx (Excel 2007+) 格式
  4. 完整示例:读取 Excel
    • 读取 .xls.xlsx 文件
    • 获取单元格数据并处理不同类型
  5. 高级功能
    • 样式设置:字体、颜色、边框、对齐方式
    • 处理大数据量SXSSFWorkbook (流式 API) 避免内存溢出
    • 合并单元格
    • 调整列宽
    • 创建下拉列表
  6. 总结与最佳实践

环境搭建

你需要在你的 Java 项目中添加 POI 的依赖,推荐使用 Maven 或 Gradle。

Maven 依赖

你需要根据你操作的 Excel 版本选择不同的依赖模块,现在推荐使用 .xlsx 格式。

<dependencies>
    <!-- 针对 .xlsx 格式 (Office 2007+) -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.5</version> <!-- 请使用最新版本 -->
    </dependency>
    <!-- 针对 .xls 格式 (Office 97-2003) -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.2.5</version> <!-- 请使用最新版本 -->
    </dependency>
    <!-- 用于处理 XML 等相关依赖,poi-ooxml 会自动包含 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-lite</artifactId>
        <version>5.2.5</version>
    </dependency>
</dependencies>

注意poi-ooxml 已经包含了 poi 的核心功能,所以如果你只操作 .xlsx 文件,可以只添加 poi-ooxml,但为了兼容性,通常两者都加上。

Gradle 依赖

implementation 'org.apache.poi:poi-ooxml:5.2.5'
implementation 'org.apache.poi:poi:5.2.5'

核心概念

理解 POI 的对象模型是关键:

Java POI如何高效写入Excel数据?-图2
(图片来源网络,侵删)
  • Workbook: 代表一个 Excel 文件(工作簿),对于 .xlsHSSFWorkbook,对于 .xlsxXSSFWorkbook
  • Sheet: 代表 Workbook 中的一个工作表(Sheet1, Sheet2...)。
  • Row: 代表 Sheet 中的某一行,从 0 开始计数。
  • Cell: 代表 Row 中的某一个单元格,从 0 开始计数。

操作流程通常是:创建 Workbook -> 创建 Sheet -> 创建 Row -> 创建 Cell -> 写入数据 -> 保存文件。


完整示例:创建并写入 Excel

这个例子将创建一个包含学生信息的 Excel 文件。

1 创建 .xlsx (推荐) 格式

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class WriteExcelXlsxExample {
    public static void main(String[] args) {
        // 1. 创建一个新的 XSSFWorkbook (对应 .xlsx 格式)
        Workbook workbook = new XSSFWorkbook();
        // 2. 创建一个工作表,名为 "学生信息"
        Sheet sheet = workbook.createSheet("学生信息");
        // 3. 创建表头行 (第 0 行)
        Row headerRow = sheet.createRow(0);
        // 4. 创建表头单元格并设置值
        Cell headerCell1 = headerRow.createCell(0);
        headerCell1.setCellValue("ID");
        Cell headerCell2 = headerRow.createCell(1);
        headerCell2.setCellValue("姓名");
        Cell headerCell3 = headerRow.createCell(2);
        headerCell3.setCellValue("年龄");
        Cell headerCell4 = headerRow.createCell(3);
        headerCell4.setCellValue("分数");
        // 5. 创建数据行
        Object[][] studentsData = {
                {1, "张三", 20, 95.5},
                {2, "李四", 21, 88.0},
                {3, "王五", 19, 76.5}
        };
        for (int i = 0; i < studentsData.length; i++) {
            Row dataRow = sheet.createRow(i + 1); // 从第 1 行开始写数据
            Object[] student = studentsData[i];
            for (int j = 0; j < student.length; j++) {
                Cell cell = dataRow.createCell(j);
                // 根据数据类型设置单元格值
                if (student[j] instanceof String) {
                    cell.setCellValue((String) student[j]);
                } else if (student[j] instanceof Integer) {
                    cell.setCellValue((Integer) student[j]);
                } else if (student[j] instanceof Double) {
                    cell.setCellValue((Double) student[j]);
                }
            }
        }
        // 6. 设置列宽,防止内容被截断
        sheet.autoSizeColumn(0);
        sheet.autoSizeColumn(1);
        sheet.autoSizeColumn(2);
        sheet.autoSizeColumn(3);
        // 7. 写入文件
        try (FileOutputStream fileOut = new FileOutputStream("students.xlsx")) {
            workbook.write(fileOut);
            System.out.println("Excel 文件 students.xlsx 创建成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 8. 关闭工作簿,释放资源
            try {
                if (workbook != null) {
                    workbook.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2 创建 .xls 格式

代码几乎完全一样,只需要将 XSSFWorkbook 替换为 HSSFWorkbook,文件名后缀改为 .xls

// 将 XSSFWorkbook 替换为 HSSFWorkbook
Workbook workbook = new HSSFWorkbook();
// ...
// 文件名改为 students.xls
FileOutputStream fileOut = new FileOutputStream("students.xls");

完整示例:读取 Excel

这个例子将读取上面创建的 students.xlsx 文件。

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
public class ReadExcelExample {
    public static void main(String[] args) {
        String filePath = "students.xlsx";
        try (FileInputStream fileIn = new FileInputStream(filePath);
             Workbook workbook = new XSSFWorkbook(fileIn)) { // 自动识别 .xlsx 或 .xls
            // 1. 获取第一个工作表
            Sheet sheet = workbook.getSheetAt(0); // 也可以用 workbook.getSheet("学生信息");
            // 2. 遍历每一行
            for (Row row : sheet) {
                // 跳过表头
                if (row.getRowNum() == 0) {
                    continue;
                }
                // 3. 遍历每一个单元格
                StringBuilder rowData = new StringBuilder();
                for (Cell cell : row) {
                    // 4. 根据单元格类型获取值
                    switch (cell.getCellType()) {
                        case STRING:
                            rowData.append(cell.getStringCellValue()).append("\t");
                            break;
                        case NUMERIC:
                            // 检查是整数还是日期
                            if (DateUtil.isCellDateFormatted(cell)) {
                                rowData.append(cell.getDateCellValue()).append("\t");
                            } else {
                                rowData.append(cell.getNumericCellValue()).append("\t");
                            }
                            break;
                        case BOOLEAN:
                            rowData.append(cell.getBooleanCellValue()).append("\t");
                            break;
                        case FORMULA:
                            rowData.append(cell.getCellFormula()).append("\t");
                            break;
                        case BLANK:
                            rowData.append("[BLANK]").append("\t");
                            break;
                        default:
                            rowData.append("[UNKNOWN]").append("\t");
                    }
                }
                System.out.println(rowData.toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

高级功能

1 样式设置

通过 CellStyle 可以设置单元格的字体、颜色、边框、背景、对齐方式等。

// ... 在创建 Cell 之前 ...
// 1. 创建一个样式
CellStyle style = workbook.createCellStyle();
// 2. 设置字体
Font font = workbook.createFont();
font.setFontName("Arial");
font.setFontHeightInPoints((short) 12);
font.setBold(true);
font.setColor(IndexedColors.RED.getIndex());
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);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
style.setRightBorderColor(IndexedColors.BLACK.getIndex());
// 5. 设置背景色
style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 6. 将样式应用到单元格
Cell cell = row.createCell(0);
cell.setCellValue("带样式的文本");
cell.setCellStyle(style);

2 处理大数据量 (避免 OOM)

当处理几十万甚至上百万行数据时,XSSFWorkbook 会将所有数据加载到内存中,导致内存溢出,此时必须使用 SXSSFWorkbook (Streaming Usermodel)。

SXSSFWorkbook 采用一种“滑动窗口”的机制,它只保留一部分数据在内存中,其余数据临时写入磁盘。

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import java.io.FileOutputStream;
import java.io.IOException;
public class WriteLargeExcelExample {
    public static void main(String[] args) {
        // 1. 创建 SXSSFWorkbook,参数 100 表示在内存中保留的行数
        Workbook workbook = new SXSSFWorkbook(100); // <-- 关键
        // 2. 创建工作表
        Sheet sheet = workbook.createSheet("大数据");
        // 3. 写入数据 (模拟 10 万行)
        for (int rowNum = 0; rowNum < 100000; rowNum++) {
            Row row = sheet.createRow(rowNum);
            for (int colNum = 0; colNum < 10; colNum++) {
                Cell cell = row.createCell(colNum);
                cell.setCellValue("Row " + rowNum + ", Col " + colNum);
            }
        }
        // 4. 写入文件
        try (FileOutputStream fileOut = new FileOutputStream("large_data.xlsx")) {
            workbook.write(fileOut);
            System.out.println("大数据 Excel 文件创建成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5. 清理临时文件 (非常重要!)
            ((SXSSFWorkbook) workbook).dispose();
            try {
                if (workbook != null) {
                    workbook.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

注意

  • SXSSFWorkbook 只支持 .xlsx 格式
  • 不支持 Sheet.clone()Workbook.cloneSheet()
  • 不支持公式计算。
  • 写入完成后,必须调用 dispose() 方法来删除临时文件。

3 合并单元格

// ...
Sheet sheet = workbook.createSheet("合并单元格示例");
// 合并从 (0,0) 到 (2,3) 的区域
sheet.addMergedRegion(new CellRangeAddress(0, 2, 0, 3));
// 在合并后的区域的左上角单元格写入内容
Cell mergedCell = sheet.createRow(0).createCell(0);
mergedCell.setCellValue("这是一个合并的大单元格");

4 调整列宽

// ...
Sheet sheet = workbook.createSheet("调整列宽");
// 自动调整所有列的宽度(基于内容)
sheet.autoSizeColumn(0);
sheet.autoSizeColumn(1);
// 或者手动设置列宽(单位是 1/256 个字符的宽度)
sheet.setColumnWidth(0, 30 * 256); // 设置第 0 列宽度为 30 个字符
sheet.setColumnWidth(1, 20 * 256); // 设置第 1 列宽度为 20 个字符

5 创建下拉列表

// ...
Sheet sheet = workbook.createSheet("下拉列表");
// 1. 创建一个隐藏的工作表来存放下拉列表的选项
Sheet hiddenSheet = workbook.createSheet("hidden_sheet");
hiddenSheet.createRow(0).createCell(0).setCellValue("选项1");
hiddenSheet.createRow(1).createCell(0).setCellValue("选项2");
hiddenSheet.createRow(2).createCell(0).setCellValue("选项3");
// 2. 定义名称,指向这些选项
Name name = workbook.createName();
name.setNameName("dropdownList");
name.setRefersToFormula("hidden_sheet!$A$1:$A$3");
// 3. 在主工作表的 A1 单元格创建下拉列表
DataValidationHelper validationHelper = sheet.getDataValidationHelper();
DataValidationConstraint constraint = validationHelper.createFormulaListConstraint("dropdownList");
CellRangeAddressList addressList = new CellRangeAddressList(0, 0, 0, 0); // A1 单元格
DataValidation validation = validationHelper.createValidation(constraint, addressList);
sheet.addValidationData(validation);

总结与最佳实践

  1. 选择正确的 API

    • 旧版 .xls -> HSSFWorkbook
    • 新版 .xlsx -> XSSFWorkbook
    • 大数据量 .xlsx -> SXSSFWorkbook (必选)
  2. 资源管理:始终使用 try-with-resources 语句来关闭 FileInputStreamWorkbook,确保文件句柄和内存被正确释放。

  3. 样式复用:不要为每个单元格都创建一个新的 CellStyle,如果多个单元格需要相同的样式,请创建一个 CellStyle 对象并重复使用它,以节省内存。

  4. 数据类型:写入数据时,尽量使用 setCellValue 的强类型方法(如 setCellValue(String value)),而不是依赖自动转换,这更可靠。

  5. 异常处理:文件操作是 I/O 密集型任务,务必进行充分的异常处理。

希望这份详细的指南能帮助你掌握 Java POI 操作 Excel!

分享:
扫描分享到社交APP
上一篇
下一篇