杰瑞科技汇

Java POI如何生成Word文档?

准备工作:添加 POI 依赖

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

Java POI如何生成Word文档?-图1
(图片来源网络,侵删)
<dependencies>
    <!-- 核心 POI 库 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.2.5</version> <!-- 建议使用较新版本 -->
    </dependency>
    <!-- 处理 OOXML 格式 (如 .docx, .xlsx) 的核心库 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.5</version>
    </dependency>
    <!-- 为了处理文档中的图片,需要这个依赖 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>ooxml-schemas</artifactId>
        <version>1.4</version>
    </dependency>
</dependencies>

如果你使用 Gradle,则在 build.gradle 文件中添加:

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

核心概念

在开始编码前,了解几个核心类非常重要:

  • XWPFDocument: 代表一个 .docx 文档对象,它是所有操作的起点和容器,你可以用它来创建一个新文档,或者加载一个已有的 .docx 文件。
  • XWPFParagraph: 代表文档中的一个段落,你可以向段落中添加文本、图片、超链接等。
  • XWPFRun: 代表段落中具有相同格式的一段连续文本,一个段落可以包含多个 XWPFRun,每个 Run 可以设置自己的字体、颜色、大小等样式。
  • XWPFTable: 代表文档中的一个表格。
  • XWPFTableRow: 代表表格中的一行。
  • XWPFTableCell: 代表表格中的一个单元格。

基本流程:

  1. 创建 XWPFDocument 对象。
  2. 通过 document.createParagraph() 创建段落。
  3. 通过 paragraph.createRun() 创建文本运行块。
  4. 通过 run.setText() 设置文本内容,并可以设置样式。
  5. (可选)创建表格、插入图片等。
  6. 通过 document.write() 将文档写入输出流(如 FileOutputStream)。
  7. 关闭所有资源。

基础操作示例

下面是一个完整的 Java 示例,展示了如何创建一个 Word 文档,并添加带样式的文本、标题和列表。

Java POI如何生成Word文档?-图2
(图片来源网络,侵删)
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 对象
        try (XWPFDocument document = new XWPFDocument()) {
            // 2. 创建一个段落并添加标题
            XWPFParagraph titleParagraph = document.createParagraph();
            titleParagraph.setAlignment(ParagraphAlignment.CENTER); // 设置居中对齐
            XWPFRun titleRun = titleParagraph.createRun();
            titleRun.setText("POI 生成 Word 文档示例");
            titleRun.setBold(true); // 设置为粗体
            titleRun.setFontFamily("微软雅黑"); // 设置字体
            titleRun.setFontSize(18); // 设置字号
            // 3. 创建一个普通段落
            XWPFParagraph paragraph1 = document.createParagraph();
            XWPFRun run1 = paragraph1.createRun();
            run1.setText("这是一个使用 Apache POI 生成的 Word 文档段落。");
            run1.setFontSize(12);
            run1.setItalic(true); // 设置为斜体
            // 4. 创建另一个段落,并添加列表
            XWPFParagraph listParagraph = document.createParagraph();
            listParagraph.setNumID(getNumId(document)); // 应用列表样式
            XWPFRun listRun1 = listParagraph.createRun();
            listRun1.setText("列表项一");
            XWPFParagraph listParagraph2 = document.createParagraph();
            listParagraph2.setNumID(getNumId(document)); // 应用相同的列表样式
            XWPFRun listRun2 = listParagraph2.createRun();
            listRun2.setText("列表项二");
            // 5. 创建一个二级标题
            XWPFParagraph heading2 = document.createParagraph();
            heading2.setSpacingAfter(200); // 段后间距
            XWPFRun heading2Run = heading2.createRun();
            heading2Run.setText("二级标题");
            heading2Run.setBold(true);
            heading2Run.setFontSize(14);
            heading2Run.setUnderline(UnderlinePatterns.SINGLE); // 添加下划线
            // 6. 将文档写入到本地文件
            try (FileOutputStream out = new FileOutputStream("D:/poi-example.docx")) {
                document.write(out);
                System.out.println("Word 文档生成成功!文件路径: D:/poi-example.docx");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 为段落应用列表样式,这是一个简单的实现,创建一个默认的编号列表。
     * @param document XWPFDocument 对象
     * @return 列表的编号 ID
     */
    private static BigInteger getNumId(XWPFDocument document) {
        // 创建一个默认的编号列表定义
        CTNumbering numbering = CTNumbering.Factory.newInstance();
        CTAbstractNum abstractNum = numbering.addNewAbstractNum();
        abstractNum.setAbstractNumId(BigInteger.valueOf(1));
        CTLevel level = abstractNum.addNewLvl();
        level.setIlvl(BigInteger.ZERO); // 第一级列表
        level.addNewStart().setVal(BigInteger.ONE); // 从 1 开始
        level.addNumFmt().setVal("decimal"); // 使用数字格式 (1, 2, 3...)
        // 将编号定义添加到文档中
        CTNumbering numberingInstance = document.getCTNumbering();
        if (numberingInstance == null) {
            document.setNumbering(numbering);
        } else {
            numberingInstance.setAbstractNum(abstractNum);
        }
        return BigInteger.valueOf(1); // 返回这个列表的 ID
    }
}

进阶操作

1 插入表格

创建和填充表格非常直观。

// ... 在创建文档之后,写入文件之前 ...
// 创建一个 3行4列 的表格
XWPFTable table = document.createTable(3, 4);
// 获取第一行
XWPFTableRow tableRowOne = table.getRow(0);
// 设置第一行的单元格文本
tableRowOne.getCell(0).setText("姓名");
tableRowOne.getCell(1).setText("年龄");
tableRowOne.getCell(2).setText("性别");
tableRowOne.getCell(3).setText("城市");
// 获取第二行
XWPFTableRow tableRowTwo = table.getRow(1);
tableRowTwo.getCell(0).setText("张三");
tableRowTwo.getCell(1).setText("30");
tableRowTwo.getCell(2).setText("男");
tableRowTwo.getCell(3).setText("北京");
// 获取第三行
XWPFTableRow tableRowThree = table.getRow(2);
tableRowThree.getCell(0).setText("李四");
tableRowThree.getCell(1).setText("25");
tableRowThree.getCell(2).setText("女");
tableRowThree.getCell(3).setText("上海");
// 可以合并单元格,例如合并第一行的后三个单元格
tableRowOne.getCell(1).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
tableRowOne.getCell(2).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
tableRowOne.getCell(3).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);

2 插入图片

插入图片需要指定图片的宽度和高度(单位是 EMU - English Metric Units, 1 英寸 = 914400 EMU)。

// ... 在创建文档之后,写入文件之前 ...
// 创建一个段落来放置图片
XWPFParagraph pictureParagraph = document.createParagraph();
XWPFRun pictureRun = pictureParagraph.createRun();
// 图片路径
String imagePath = "D:/path/to/your/image.png"; // 请替换为你的图片路径
try (java.io.InputStream picIn = new java.io.FileInputStream(imagePath)) {
    // 图片宽度 (400 EMU)
    int width = 400;
    // 图片高度 (300 EMU)
    int height = 300;
    pictureRun.addPicture(picIn, XWPFDocument.PICTURE_TYPE_PNG, "image.png", Units.toEMU(width), Units.toEMU(height));
    pictureRun.addBreak(); // 换行
} catch (Exception e) {
    e.printStackTrace();
}

完整综合示例

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

import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
public class ComprehensiveWordExample {
    public static void main(String[] args) {
        String outputFilePath = "D:/comprehensive-example.docx";
        String imagePath = "D:/path/to/your/logo.png"; // 替换为你的图片路径
        try (XWPFDocument document = new XWPFDocument()) {
            // --- 1. 标题和Logo ---
            XWPFParagraph titleP = document.createParagraph();
            titleP.setAlignment(ParagraphAlignment.CENTER);
            // 添加Logo
            try (FileInputStream picIn = new FileInputStream(imagePath)) {
                titleP.createRun().addPicture(picIn, XWPFDocument.PICTURE_TYPE_PNG, "logo.png", Units.toEMU(100), Units.toEMU(50));
            }
            XWPFRun titleR = titleP.createRun();
            titleR.addBreak(); // 换行
            titleR.setText("年度报告");
            titleR.setBold(true);
            titleR.setFontSize(24);
            titleR.setFontFamily("宋体");
            titleR.addBreak(); // 换行
            titleR.addBreak(); // 换行
            // --- 2. ---
            XWPFParagraph summaryP = document.createParagraph();
            summaryP.setSpacingAfter(200);
            XWPFRun summaryR = summaryP.createRun();
            summaryR.setText("本报告总结了2025年度的主要工作成果和财务状况。");
            summaryR.setFontSize(12);
            summaryR.setItalic(true);
            // --- 3. 列表 ---
            BigInteger numId = createNumberedList(document);
            addListItem(document, "完成了所有预定项目目标。", numId);
            addListItem(document,"实现了销售额同比增长20%。", numId);
            addListItem(document,"成功拓展了两个新的市场区域。", numId);
            // --- 4. 表格 ---
            createAndFillTable(document);
            // --- 5. 写入文件 ---
            try (FileOutputStream out = new FileOutputStream(outputFilePath)) {
                document.write(out);
                System.out.println("综合 Word 文档生成成功!文件路径: " + outputFilePath);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static void addListItem(XWPFDocument doc, String text, BigInteger numId) {
        XWPFParagraph p = doc.createParagraph();
        p.setNumID(numId);
        XWPFRun r = p.createRun();
        r.setText(text);
    }
    private static BigInteger createNumberedList(XWPFDocument doc) {
        CTNumbering numbering = CTNumbering.Factory.newInstance();
        CTAbstractNum abstractNum = numbering.addNewAbstractNum();
        abstractNum.setAbstractNumId(BigInteger.ONE);
        CTLevel level = abstractNum.addNewLvl();
        level.setIlvl(BigInteger.ZERO);
        level.addNewStart().setVal(BigInteger.ONE);
        level.addNumFmt().setVal("decimal");
        doc.getCTDocument().getBody().setNumbering(numbering);
        return BigInteger.ONE;
    }
    private static void createAndFillTable(XWPFDocument doc) {
        XWPFTable table = doc.createTable(4, 2);
        table.setTableAlignment(TableAlignment.CENTER);
        // 表头样式
        XWPFTableRow headerRow = table.getRow(0);
        headerRow.getCell(0).setText("指标");
        headerRow.getCell(1).setText("数值");
        applyHeaderStyle(headerRow);
        // 表格数据
        table.getRow(1).getCell(0).setText("总营收");
        table.getRow(1).getCell(1).setText("¥5,000,000");
        table.getRow(2).getCell(0).setText("净利润");
        table.getRow(2).getCell(1).setText("¥1,200,000");
        table.getRow(3).getCell(0).setText("员工人数");
        table.getRow(3).getCell(1).setText("150");
    }
    private static void applyHeaderStyle(XWPFTableRow row) {
        for (XWPFTableCell cell : row.getTableCells()) {
            cell.setColor("A9A9A9"); // 灰色背景
            CTTblPr tablePr = row.getTable().getCTTbl().getTblPr();
            if (tablePr == null) {
                row.getTable().getCTTbl().setTblPr(CTTblPr.Factory.newInstance());
            }
            // 可以在这里添加更复杂的样式,如边框
            cell.getCTTc().getTcPr().addNewShd().setFill("D3D3D3"); // 设置单元格背景色
            for (XWPFParagraph p : cell.getParagraphs()) {
                for (XWPFRun r : p.getRuns()) {
                    r.setBold(true);
                    r.setColor("FFFFFF"); // 白色文字
                    r.setFontFamily("微软雅黑");
                }
            }
        }
    }
}

总结与注意事项

  • .docx vs .doc: POI 主要支持 .docx 格式,对于旧版的 .doc 格式,需要使用 HWPF 库,但功能较弱且已不推荐。
  • 样式复杂性: POI 对 Word 样式的支持是有限的,特别是复杂的样式(如自定义标题样式、页眉页脚的复杂布局)可能需要直接操作底层的 XML,或者考虑使用更专业的模板引擎。
  • 模板引擎: 对于复杂的报告生成,通常推荐使用 模板引擎(如 FreeMarker, Thymeleaf)结合一个预先设计好的 Word 模板(.docx),你只需要在模板中用 ${variable} 这样的占位符标记需要替换的内容,然后引擎会根据数据生成最终的 Word 文件,这种方法更灵活、更易于维护,对于 .docx 模板,可以使用 Apache POI 的 XWPF 来读取和修改模板中的占位符。
分享:
扫描分享到社交APP
上一篇
下一篇