杰瑞科技汇

java poi 生成 word

基础准备:添加 POI 依赖

你需要在你的项目中添加 Apache POI 的相关依赖,由于 POI 的不同模块(如 Excel, Word, PowerPoint)是分开的,我们只需要引入 Word 相关的即可。

java poi 生成 word-图1
(图片来源网络,侵删)

对于 Maven 项目,在 pom.xml 文件中添加以下依赖:

<dependencies>
    <!-- Apache POI 核心依赖 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.2.4</version> <!-- 建议使用较新版本 -->
    </dependency>
    <!-- Apache POI OOXML 依赖,用于处理 .docx 格式 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.4</version>
    </dependency>
    <!-- 为了处理 XML 和其他格式,可能还需要一些依赖 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>ooxml-lite</artifactId>
        <version>1.4</version>
    </dependency>
</dependencies>

对于 Gradle 项目,在 build.gradle 文件中添加:

dependencies {
    implementation 'org.apache.poi:poi:5.2.4'
    implementation 'org.apache.poi:poi-ooxml:5.2.4'
}

POI 操作 Word 的核心概念

Apache POI 操作 Word (.docx 文件) 主要使用 XWPF (XML Word Format Processing) 包,你需要了解几个核心类:

  • XWPFDocument: 代表一个 Word 文档,它是所有操作的起点和容器。
  • XWPFParagraph: 代表 Word 文档中的一个段落。
  • XWPFRun: 代表段落中具有相同格式样式的一段连续文本,一个段落可以包含多个 Run,每个 Run 可以有不同的字体、颜色、大小等样式。
  • XWPFTable: 代表一个表格。
  • XWPFTableRow: 代表表格中的一行。
  • XWPFTableCell: 代表表格中的一个单元格。

简单关系: XWPFDocument (文档) -> XWPFParagraph (段落) -> XWPFRun (文本块) 或 XWPFTable (表格) -> XWPFTableRow (行) -> XWPFTableCell (单元格)。

java poi 生成 word-图2
(图片来源网络,侵删)

生成一个简单的 Word 文档

下面是一个最基础的例子,创建一个包含标题和正文的 Word 文档。

import org.apache.poi.xwpf.usermodel.*;
import java.io.FileOutputStream;
import java.io.IOException;
public class SimpleWordGenerator {
    public static void main(String[] args) {
        // 1. 创建一个空的 XWPFDocument 对象
        XWPFDocument document = new XWPFDocument();
        try (FileOutputStream out = new FileOutputStream("D:/simple_document.docx")) {
            // 2. 创建一个段落
            XWPFParagraph titleParagraph = document.createParagraph();
            XWPFRun titleRun = titleParagraph.createRun();
            // 3. 设置段落内容
            titleRun.setText("这是我的第一个 POI Word 文档标题");
            titleRun.setBold(true); // 设置为粗体
            titleRun.setFontSize(16); // 设置字体大小
            titleRun.setFontFamily("宋体"); // 设置字体
            // 4. 创建第二个段落(正文)
            XWPFParagraph bodyParagraph = document.createParagraph();
            XWPFRun bodyRun = bodyParagraph.createRun();
            bodyRun.setText("这是一个段落,POI 是一个非常强大的 Java 库,可以用来操作 Office 文档。");
            bodyRun.setFontSize(12);
            bodyRun.setFontFamily("宋体");
            // 5. 将文档写入到输出流
            document.write(out);
            System.out.println("Word 文档生成成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 6. 关闭文档
            try {
                document.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

运行这段代码后,你会在 D: 盘根目录下找到一个名为 simple_document.docx 的文件。


高级功能示例

下面我们来看一些更复杂的功能,包括列表、表格、图片和页眉页脚。

1 添加列表

// 在 main 方法中,在正文段落之后添加以下代码
XWPFParagraph listParagraph = document.createParagraph();
// 设置列表类型为项目符号列表
listParagraph.setNumID(1); // 注意:这里的 1 是一个预设的列表 ID,需要预先定义
XWPFRun listRun1 = listParagraph.createRun();
listRun1.setText("列表项一");
listRun1.setFontFamily("宋体");
XWPFRun listRun2 = listParagraph.createRun();
listRun2.setText("列表项二");
listRun2.setFontFamily("宋体");

注意: 直接设置 setNumID 可能需要你预先在文档中定义好列表样式,更灵活的方式是使用 CTNumPrCTAbstractNum 来动态定义,但这比较复杂,对于简单应用,直接用段落和缩进模拟列表也是可行的。

java poi 生成 word-图3
(图片来源网络,侵删)

2 添加表格

// 在 main 方法中添加
// 创建一个 3x3 的表格
XWPFTable table = document.createTable(3, 3);
// 获取第一行
XWPFTableRow row1 = table.getRow(0);
// 设置第一行的单元格
row1.getCell(0).setText("姓名");
row1.getCell(1).setText("年龄");
row1.getCell(2).setText("城市");
// 获取第二行
XWPFTableRow row2 = table.getRow(1);
row2.getCell(0).setText("张三");
row2.getCell(1).setText("30");
row2.getCell(2).setText("北京");
// 获取第三行
XWPFTableRow row3 = table.getRow(2);
row3.getCell(0).setText("李四");
row3.getCell(1).setText("25");
row3.getCell(2).setText("上海");
// 可以设置表格样式,例如合并单元格
// table.getRow(0).getCell(0).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
// table.getRow(1).getCell(0).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);

3 插入图片

// 在 main 方法中添加
XWPFParagraph imageParagraph = document.createParagraph();
XWPFRun imageRun = imageParagraph.createRun();
try {
    // 读取图片文件
    String imagePath = "D:/path/to/your/image.png"; // 替换为你的图片路径
    // 图片宽度(单位:twip, 1 cm = 567 twip)
    int width = 300 * 100; // 300像素
    // 图片高度(单位:twip)
    int height = 200 * 100; // 200像素
    // 插入图片
    imageRun.addPicture(new FileInputStream(imagePath), XWPFDocument.PICTURE_TYPE_PNG, imagePath, Units.toEMU(width), Units.toEMU(height));
    imageRun.addBreak(); // 换行
} catch (Exception e) {
    e.printStackTrace();
}

注意: 你需要 import org.apache.poi.util.Units;

4 添加页眉和页脚

// 在 main 方法中,在 document.write(out) 之前添加
// 添加页眉
XWPFHeader header = document.createHeader(HeaderFooterType.DEFAULT);
XWPFParagraph headerPara = header.createParagraph();
XWPFRun headerRun = headerPara.createRun();
headerRun.setText("这是页眉内容");
headerRun.setBold(true);
// 添加页脚
XWPFFooter footer = document.createFooter(HeaderFooterType.DEFAULT);
XWPFParagraph footerPara = footer.createParagraph();
XWPFRun footerRun = footerPara.createRun();
footerRun.setText("第 1 页"); // POI 对页码的动态支持较弱,通常需要手动处理或使用更复杂的库

完整的高级示例代码

下面是一个结合了上述所有功能的完整示例。

import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTNumPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTAbstractNum;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDecimalNumber;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class AdvancedWordGenerator {
    public static void main(String[] args) {
        // 1. 创建文档
        XWPFDocument document = new XWPFDocument();
        // 2. 添加标题
        XWPFParagraph title = document.createParagraph();
        XWPFRun titleRun = title.createRun();
        titleRun.setText("高级 POI Word 文档示例");
        titleRun.setBold(true);
        titleRun.setFontSize(20);
        titleRun.setFontFamily("微软雅黑");
        titleRun.addBreak(); // 换行
        // 3. 添加正文段落
        XWPFParagraph para1 = document.createParagraph();
        XWPFRun para1Run = para1.createRun();
        para1Run.setText("这是一个包含多种元素的段落,下面将展示列表、表格和图片。");
        para1Run.setFontSize(12);
        para1Run.setFontFamily("宋体");
        // 4. 添加列表(简化版,通过缩进模拟)
        XWPFParagraph listPara1 = document.createParagraph();
        listPara1.setIndentationLeft(500); // 设置左缩进
        XWPFRun listRun1 = listPara1.createRun();
        listRun1.setText("这是列表项 1");
        listRun1.setFontFamily("宋体");
        XWPFParagraph listPara2 = document.createParagraph();
        listPara2.setIndentationLeft(500);
        XWPFRun listRun2 = listPara2.createRun();
        listRun2.setText("这是列表项 2");
        listRun2.setFontFamily("宋体");
        // 5. 添加表格
        XWPFTable table = document.createTable(3, 3);
        table.setWidth("100%"); // 设置表格宽度为页面宽度
        // 设置表格样式(可选)
        // table.setTableAlignment(TableRowAlign.CENTER);
        // 填充表格数据
        String[][] tableData = {
                {"姓名", "年龄", "职业"},
                {"王五", "28", "软件工程师"},
                {"赵六", "32", "产品经理"}
        };
        for (int i = 0; i < table.getNumberOfRows(); i++) {
            XWPFTableRow row = table.getRow(i);
            for (int j = 0; j < table.getNumberOfColumns(); j++) {
                XWPFTableCell cell = row.getCell(j);
                cell.setText(tableData[i][j]);
                // 设置单元格内容居中
                cell.getCTTc().getTcPr().addNewJc().setVal(org.openxmlformats.schemas.wordprocessingml.x2006.STJc.CENTER);
            }
        }
        // 6. 添加图片(请确保图片路径存在)
        XWPFParagraph imagePara = document.createParagraph();
        imagePara.setAlignment(ParagraphAlignment.CENTER); // 图片居中
        XWPFRun imageRun = imagePara.createRun();
        try {
            String imagePath = "D:/logo.png"; // 替换为你的图片路径
            imageRun.addPicture(new FileInputStream(imagePath), XWPFDocument.PICTURE_TYPE_PNG, imagePath, Units.toEMU(300), Units.toEMU(150));
        } catch (Exception e) {
            System.err.println("图片路径错误或文件不存在,已跳过图片插入。");
            e.printStackTrace();
        }
        // 7. 添加页眉和页脚
        XWPFHeader header = document.createHeader(HeaderFooterType.DEFAULT);
        XWPFParagraph headerPara = header.createParagraph();
        XWPFRun headerRun = headerPara.createRun();
        headerRun.setText("公司机密 - 请勿外传");
        headerRun.setItalic(true);
        XWPFFooter footer = document.createFooter(HeaderFooterType.DEFAULT);
        XWPFParagraph footerPara = footer.createParagraph();
        XWPFRun footerRun = footerPara.createRun();
        footerRun.setText("© 2025 My Company");
        footerRun.setSubscript(VerticalAlign.SUPERSCRIPT); // 设置为上标
        // 8. 保存文档
        try (FileOutputStream out = new FileOutputStream("D:/advanced_document.docx")) {
            document.write(out);
            System.out.println("高级 Word 文档生成成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                document.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

重要注意事项与替代方案

1 POI 的局限性

  • .doc 格式: POI 对旧版 Word 文档(.doc)的支持非常有限且不稳定。强烈建议只使用 .docx 格式
  • 样式复杂度: 对于非常复杂的样式(如复杂的页眉页脚、图文混排、样式继承等),POI 的 API 会变得非常繁琐,难以控制。
  • 模板处理: 直接用代码生成所有内容很灵活,但当文档结构非常复杂时,代码会变得臃肿且难以维护,更常见的做法是使用模板。

2 使用模板(推荐方案)

对于生产环境,最佳实践是使用 Word 模板

  1. 创建模板: 在 Word 中设计好你的文档框架,包括固定的文字、Logo、表格结构、页眉页脚等,将需要动态替换的内容(如姓名、日期)用 ${name}, ${date} 这样的占位符标记。
  2. 使用 POI 操作模板: 使用 POI 打开这个 .docx 模板文件。
  3. 替换占位符: 遍历文档中的所有段落和文本块,找到 形式的占位符,并用你的实际数据替换它。
  4. 填充表格: 找到模板中的表格,然后根据数据动态添加行。
  5. 保存结果: 将处理后的文档另存为一个新的文件。

这种方法将样式设计和数据逻辑分离,大大提高了开发效率和文档的可维护性。

3 其他强大的替代库

POI 无法满足你的需求,可以考虑以下库:

  • docx4j: 一个功能非常强大的 Java 库,专门用于处理 OpenXML 格式(.docx, .xlsx, .pptx),它的 API 比 POI 更面向对象,对复杂样式的支持更好。
  • iText: 虽然 iText 以 PDF 处理闻名,但它也有一个 iText for Word (iText 5) 的版本,功能也很强大,iText 5 的商业使用需要付费许可证,iText 7 的 Word 处理模块是商业产品。
功能 核心类/方法 描述
创建文档 new XWPFDocument() 创建一个空的 Word 文档对象。
创建段落 document.createParagraph() 在文档中添加一个新段落。
创建文本块 paragraph.createRun() 在段落中创建一个具有相同样式的文本块。
设置文本 run.setText("内容") 为文本块设置内容。
设置样式 run.setBold(true), run.setFontSize(12) 设置文本的粗体、字体大小等样式。
创建表格 document.createTable(rows, cols) 创建一个指定行数和列数的表格。
操作单元格 row.getCell(col).setText("内容") 获取指定单元格并设置内容。
插入图片 run.addPicture(...) 在文本块中插入图片,需要指定图片路径、类型和尺寸。
添加页眉/页脚 document.createHeader/Footer(...) 为文档添加页眉或页脚。

希望这份详细的指南能帮助你顺利地使用 Java POI 生成 Word 文档!从简单的例子开始,逐步尝试更复杂的功能,你会很快掌握它。

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