杰瑞科技汇

Java用POI写Excel,具体怎么操作?

什么是 Apache POI?

Apache POI (Poor Obfuscation Implementation) 是一个开源的 Java 库,由 Apache 软件基金会维护,它允许程序员使用 Java 代码来操作 Microsoft Office 格式的文件,

Java用POI写Excel,具体怎么操作?-图1
(图片来源网络,侵删)
  • Excel (.xls, .xlsx)
  • Word (.doc, .docx)
  • PowerPoint (.ppt, .pptx)

对于 Excel 操作,POI 提供了不同的模型来处理不同版本的 Excel 文件:

文件扩展名 POI 模型 描述
.xls HSSF (Horrible SpreadSheet Format) 用于操作 Excel 97-2003 格式,基于二进制格式,行数和列数限制为 65536 x 256。
.xlsx XSSF (XML SpreadSheet Format) 用于操作 Office Open XML (OOXML) 格式,即 Excel 2007 及以后版本,基于 XML 格式,理论上行和列的数量只受内存限制。
.xlsb SXSSF (Streaming Usermodel API for XSSF) 用于处理超大 .xlsx 文件,它采用流式处理,将部分数据写入临时文件,从而减少内存消耗。

环境配置 (Maven)

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

<dependencies>
    <!-- 核心依赖 -->
    <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> <!-- 用于操作 .xlsx 格式 -->
    </dependency>
    <!-- 为了支持 XSSF 和 SXSSF 的一些高级特性,可能需要这个 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-scratchpad</artifactId>
        <version>5.2.5</version>
    </dependency>
</dependencies>

基础写入操作 (创建 Excel 文件)

下面是一个完整的示例,演示如何创建一个 .xlsx 文件,并向其中写入数据、设置样式和公式。

目标: 创建一个名为 员工信息.xlsx 的文件,内容如下:

Java用POI写Excel,具体怎么操作?-图2
(图片来源网络,侵删)
姓名 部门 工资 奖金 总收入 (公式)
张三 技术部 8000 1000 =C2+D2
李四 销售部 7000 2000 =C3+D3
王五 人事部 6000 500 =C4+D4
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class ExcelWriteExample {
    public static void main(String[] args) {
        // 1. 创建一个新的 XSSFWorkbook 对象 (代表一个 .xlsx 文件)
        Workbook workbook = new XSSFWorkbook();
        // 2. 创建一个 Sheet (工作表)
        Sheet sheet = workbook.createSheet("员工信息");
        // 3. 创建一个样式,用于表头
        CellStyle headerStyle = workbook.createCellStyle();
        Font headerFont = workbook.createFont();
        headerFont.setBold(true);
        headerFont.setFontHeightInPoints((short) 12);
        headerStyle.setFont(headerFont);
        headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        // 4. 创建行和单元格,并写入表头数据
        Row headerRow = sheet.createRow(0); // 第 0 行
        String[] headers = {"姓名", "部门", "工资", "奖金", "总收入"};
        for (int i = 0; i < headers.length; i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers[i]);
            cell.setCellStyle(headerStyle);
        }
        // 5. 写入数据行
        Object[][] data = {
                {"张三", "技术部", 8000, 1000},
                {"李四", "销售部", 7000, 2000},
                {"王五", "人事部", 6000, 500}
        };
        for (int i = 0; i < data.length; i++) {
            Row row = sheet.createRow(i + 1); // 从第 1 行开始
            for (int j = 0; j < data[i].length; j++) {
                // 写入姓名、部门、工资、奖金
                row.createCell(j).setCellValue(String.valueOf(data[i][j]));
            }
            // 写入公式
            Cell totalCell = row.createCell(4);
            totalCell.setCellFormula("C" + (i + 2) + "+D" + (i + 2));
        }
        // 6. 自动调整列宽,使内容完整显示
        for (int i = 0; i < headers.length; i++) {
            sheet.autoSizeColumn(i);
        }
        // 7. 将 workbook 写入到文件中
        try (FileOutputStream fileOut = new FileOutputStream("员工信息.xlsx")) {
            workbook.write(fileOut);
            System.out.println("Excel 文件 '员工信息.xlsx' 创建成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 8. 关闭 workbook,释放资源
            try {
                if (workbook != null) {
                    workbook.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

代码解析:

  1. Workbook workbook = new XSSFWorkbook();: 创建一个 .xlsx 格式的工作簿。
  2. Sheet sheet = workbook.createSheet("员工信息");: 创建一个名为 "员工信息" 的工作表。
  3. CellStyleFont: 用于设置单元格的样式,如字体加粗、背景色等。
  4. Row row = sheet.createRow(0);: 创建一行,参数 0 表示第一行。
  5. Cell cell = row.createCell(0);: 在指定行中创建一个单元格,参数 0 表示第一列。
  6. cell.setCellValue("姓名");: 设置单元格的值。
  7. cell.setCellStyle(headerStyle);: 为单元格应用之前创建的样式。
  8. totalCell.setCellFormula("C2+D2");: 设置单元格的值为 Excel 公式,注意,这里的行号是相对于工作表的,需要动态计算。
  9. sheet.autoSizeColumn(i);: 自动调整列宽,让所有内容都能显示出来。
  10. FileOutputStreamworkbook.write(): 将内存中的 Workbook 对象写入到磁盘文件。
  11. try-with-resourcesworkbook.close(): 非常重要! 确保文件流和 Workbook 资源被正确关闭,防止内存泄漏。

基础读取操作 (读取 Excel 文件)

下面是如何读取上面创建的 员工信息.xlsx 文件。

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
public class ExcelReadExample {
    public static void main(String[] args) {
        String filePath = "员工信息.xlsx";
        try (FileInputStream fileIn = new FileInputStream(filePath);
             Workbook workbook = new XSSFWorkbook(fileIn)) {
            // 1. 获取第一个工作表
            Sheet sheet = workbook.getSheetAt(0); // 或者 getSheet("员工信息")
            // 2. 获取总行数
            int rowCount = sheet.getPhysicalNumberOfRows();
            System.out.println("总行数: " + rowCount);
            // 3. 遍历每一行
            for (int r = 0; r < rowCount; r++) {
                Row row = sheet.getRow(r);
                if (row == null) {
                    continue; // 跳过空行
                }
                // 4. 获取总列数
                int cellCount = row.getPhysicalNumberOfCells();
                // System.out.println("第 " + (r+1) + " 行有 " + cellCount + " 列");
                // 5. 遍历每一个单元格
                StringBuilder rowData = new StringBuilder();
                for (int c = 0; c < cellCount; c++) {
                    Cell cell = row.getCell(c);
                    if (cell == null) {
                        rowData.append("\t"); // 单元格为空,用制表符分隔
                        continue;
                    }
                    // 根据单元格类型获取值
                    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.getNumericCellValue()).append("\t");
                            break;
                        case BLANK:
                            rowData.append("\t");
                            break;
                        default:
                            rowData.append("未知类型\t");
                    }
                }
                System.out.println(rowData.toString().trim());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码解析:

  1. FileInputStream: 用于读取文件。
  2. Workbook workbook = new XSSFWorkbook(fileIn);: 将文件流加载到 Workbook 对象中。
  3. sheet.getSheetAt(0): 获取第一个工作表。
  4. sheet.getPhysicalNumberOfRows(): 获取工作表中包含数据的行数。
  5. row.getCell(c): 获取指定位置的单元格。
  6. cell.getCellType(): 非常重要! 必须先判断单元格的类型,再调用对应的方法获取值,数字单元格用 getNumericCellValue(),字符串单元格用 getStringCellValue()
  7. DateUtil.isCellDateFormatted(cell): 判断一个数字格式的单元格是否代表日期。
  8. cell.getNumericCellValue(): 对于公式单元格,此方法会返回公式的计算结果。

高级技巧与注意事项

1 处理超大文件 (SXSSF)

当处理成千上万行数据时,XSSF 会消耗大量内存,甚至导致 OutOfMemoryError,这时应该使用 SXSSF (Streaming API)。

核心思想: SXSSF 只保留一定数量的行在内存中(100 行),当超过这个数量时,最早的一批行会被写入临时文件,这极大地降低了内存消耗。

// 使用 SXSSFWorkbook
// 参数 100 表示在内存中保留的行数
Workbook workbook = new SXSSFWorkbook(100); 
// ... 写入数据的代码与 XSSF 基本相同 ...
// 写入完成后,必须调用 write() 和 dispose() 来清理临时文件
try (FileOutputStream out = new FileOutputStream("large_file.xlsx")) {
    workbook.write(out);
} finally {
    ((SXSSFWorkbook) workbook).dispose(); // 清理临时文件
}

2 设置单元格样式

除了字体和背景色,你还可以设置很多样式:

CellStyle style = workbook.createCellStyle();
// 对齐方式
style.setAlignment(HorizontalAlignment.CENTER); // 水平居中
style.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
// 边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
// 背景色
style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 日期格式
CreationHelper createHelper = workbook.getCreationHelper();
style.setDataFormat(createHelper.createDataFormat().getFormat("yyyy-mm-dd hh:mm:ss"));
// 应用样式
cell.setCellStyle(style);

3 合并单元格

// 参数:起始行, 结束行, 起始列, 结束列
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 4)); // 合并第 0 行的 0 到 4 列
// 然后在合并区域的左上角单元格写入内容即可

4 处理不同日期格式

Excel 内部将日期存储为一个数字(从 1900/1/1 开始的天数),你需要用 DataFormat 来告诉 Excel 如何显示这个数字。

CellStyle dateStyle = workbook.createCellStyle();
CreationHelper createHelper = workbook.getCreationHelper();
dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy年mm月dd日"));
Cell dateCell = row.createCell(0);
dateCell.setCellValue(new Date()); // 设置当前日期
dateCell.setCellStyle(dateStyle); // 应用日期格式

5 性能建议

  1. 减少样式对象创建:如果多个单元格需要相同的样式,请复用 CellStyle 对象,而不是为每个单元格都创建一个新的。
  2. 关闭资源:始终使用 try-with-resources 或在 finally 块中关闭 WorkbookFileOutputStream
  3. 选择合适的 API:处理小文件用 XSSF,处理大文件用 SXSSF
  4. 避免频繁的行/列操作:在写入大量数据前,可以先规划好行和列,而不是动态地创建。

希望这份详细的指南能帮助你熟练掌握使用 Java 和 Apache POI 操作 Excel!

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