使用Apache POI操作Word文档:从入门到实战

Apache POI是Java生态中最流行的Microsoft Office文档操作库之一,它为Word文档(包括传统的.doc格式和现代的.docx格式)提供了全面的API支持。本文将详细介绍如何使用Apache POI创建、读取和修改Word文档。

一、Apache POI简介与环境准备

1. Apache POI组件

Apache POI包含多个组件用于处理不同Office文档:

  • POI-HWPF:处理.doc格式(Word 97-2003)
  • POI-XWPF:处理.docx格式(Word 2007及以后版本)
  • POI-HSLF:处理PowerPoint
  • POI-HSSF/XSSF:处理Excel

2. 添加Maven依赖

xml 复制代码
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>5.2.3</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.3</version>
</dependency>

3. 核心类介绍

  • XWPFDocument:代表整个Word文档
  • XWPFParagraph:文档中的段落
  • XWPFRun:段落中的文本运行(具有相同格式的文本段)
  • XWPFTable:表格
  • XWPFPicture:图片

二、创建Word文档

1. 创建简单文档

java 复制代码
import org.apache.poi.xwpf.usermodel.*;

import java.io.FileOutputStream;
import java.io.IOException;

public class SimpleDocumentCreator {
    public static void main(String[] args) throws IOException {
        // 1. 创建空文档
        XWPFDocument document = new XWPFDocument();
        
        // 2. 创建段落
        XWPFParagraph title = document.createParagraph();
        title.setAlignment(ParagraphAlignment.CENTER);
        
        // 3. 创建文本运行并设置样式
        XWPFRun titleRun = title.createRun();
        titleRun.setText("Apache POI示例文档");
        titleRun.setBold(true);
        titleRun.setFontSize(16);
        
        // 4. 添加正文段落
        XWPFParagraph body = document.createParagraph();
        body.setAlignment(ParagraphAlignment.LEFT);
        body.setIndentationFirstLine(600); // 首行缩进
        
        XWPFRun bodyRun = body.createRun();
        bodyRun.setText("这是使用Apache POI创建的Word文档。");
        bodyRun.addBreak(); // 换行
        bodyRun.setText("POI支持丰富的文本格式设置。");
        
        // 5. 保存文档
        try (FileOutputStream out = new FileOutputStream("SimpleDocument.docx")) {
            document.write(out);
        }
        
        document.close();
        System.out.println("文档创建成功!");
    }
}

2. 添加复杂格式

java 复制代码
// 创建带格式的段落
XWPFParagraph styledPara = document.createParagraph();
styledPara.setAlignment(ParagraphAlignment.BOTH); // 两端对齐

XWPFRun styledRun = styledPara.createRun();
styledRun.setText("格式丰富的文本:");
styledRun.setColor("FF0000"); // 红色
styledRun.setFontFamily("宋体");
styledRun.addBreak();

// 添加不同格式的文本
styledRun = styledPara.createRun();
styledRun.setText("粗体+斜体");
styledRun.setBold(true);
styledRun.setItalic(true);
styledRun.addTab(); // 制表符

styledRun = styledPara.createRun();
styledRun.setText("下划线");
styledRun.setUnderline(UnderlinePatterns.SINGLE);

三、操作表格

1. 创建表格

java 复制代码
// 创建3行4列的表格
XWPFTable table = document.createTable(3, 4);

// 设置表格宽度
table.setWidth("100%");

// 填充表头
XWPFTableRow headerRow = table.getRow(0);
headerRow.getCell(0).setText("序号");
headerRow.getCell(1).setText("姓名");
headerRow.getCell(2).setText("年龄");
headerRow.getCell(3).setText("部门");

// 填充数据
for (int i = 1; i < 3; i++) {
    XWPFTableRow row = table.getRow(i);
    row.getCell(0).setText(String.valueOf(i));
    row.getCell(1).setText("员工" + i);
    row.getCell(2).setText(String.valueOf(20 + i));
    row.getCell(3).setText(i % 2 == 0 ? "技术部" : "市场部");
}

// 合并单元格
table.getRow(2).getCell(3).setText("备注:所有部门");
table.addNewColSpan(2, 3, 2); // 合并第3行的第3-4列

2. 表格样式设置

java 复制代码
// 设置表格边框
CTTblPr tblPr = table.getCTTbl().getTblPr();
CTBorder border = tblPr.addNewTblBorders();
border.addNewBottom().setVal(STBorder.SINGLE);
border.addNewTop().setVal(STBorder.SINGLE);
border.addNewLeft().setVal(STBorder.SINGLE);
border.addNewRight().setVal(STBorder.SINGLE);
border.addNewInsideH().setVal(STBorder.SINGLE);
border.addNewInsideV().setVal(STBorder.SINGLE);

// 设置表头背景色
for (XWPFTableCell cell : headerRow.getTableCells()) {
    cell.setColor("D3D3D3"); // 浅灰色背景
}

四、插入图片

java 复制代码
import org.apache.poi.util.Units;

// 从文件插入图片
try (FileInputStream is = new FileInputStream("logo.png")) {
    XWPFParagraph imagePara = document.createParagraph();
    imagePara.setAlignment(ParagraphAlignment.CENTER);
    
    XWPFRun imageRun = imagePara.createRun();
    imageRun.addPicture(is, 
                       Document.PICTURE_TYPE_PNG, 
                       "logo.png", 
                       Units.toEMU(200), // 宽度
                       Units.toEMU(100)); // 高度
}

// 从字节数组插入图片
byte[] imageData = getImageData(); // 获取图片字节数组的方法
XWPFRun imageRun = document.createParagraph().createRun();
imageRun.addPicture(new ByteArrayInputStream(imageData),
                   Document.PICTURE_TYPE_JPEG,
                   "photo.jpg",
                   Units.toEMU(150),
                   Units.toEMU(150));

五、读取和修改现有文档

1. 读取文档内容

java 复制代码
public void readDocument(String filePath) throws IOException {
    try (FileInputStream fis = new FileInputStream(filePath);
         XWPFDocument document = new XWPFDocument(fis)) {
        
        System.out.println("==== 段落 ====");
        for (XWPFParagraph para : document.getParagraphs()) {
            System.out.println(para.getText());
        }
        
        System.out.println("\n==== 表格 ====");
        for (XWPFTable table : document.getTables()) {
            for (XWPFTableRow row : table.getRows()) {
                for (XWPFTableCell cell : row.getTableCells()) {
                    System.out.print(cell.getText() + "\t");
                }
                System.out.println();
            }
        }
    }
}

2. 修改现有文档

java 复制代码
public void modifyDocument(String inputPath, String outputPath) throws IOException {
    try (FileInputStream fis = new FileInputStream(inputPath);
         XWPFDocument document = new XWPFDocument(fis)) {
        
        // 修改第一个段落
        XWPFParagraph firstPara = document.getParagraphs().get(0);
        if (firstPara != null) {
            XWPFRun run = firstPara.createRun();
            run.setText("\n(本文件由系统自动生成)");
            run.setItalic(true);
            run.setColor("808080");
        }
        
        // 在文档末尾添加新内容
        XWPFParagraph newPara = document.createParagraph();
        newPara.setAlignment(ParagraphAlignment.RIGHT);
        XWPFRun newRun = newPara.createRun();
        newRun.setText("生成时间: " + LocalDate.now());
        newRun.addBreak(BreakType.PAGE); // 分页符
        
        // 保存修改后的文档
        try (FileOutputStream fos = new FileOutputStream(outputPath)) {
            document.write(fos);
        }
    }
}

六、高级功能

1. 页眉页脚设置

java 复制代码
// 创建页眉
XWPFHeader header = document.createHeader(HeaderFooterType.DEFAULT);
XWPFParagraph headerPara = header.createParagraph();
headerPara.setAlignment(ParagraphAlignment.RIGHT);
XWPFRun headerRun = headerPara.createRun();
headerRun.setText("公司机密");
headerRun.setColor("FF0000");

// 创建页脚
XWPFFooter footer = document.createFooter(HeaderFooterType.DEFAULT);
XWPFParagraph footerPara = footer.createParagraph();
footerPara.setAlignment(ParagraphAlignment.CENTER);
XWPFRun footerRun = footerPara.createRun();
footerRun.setText("页码: ");
footerRun.getCTR().addNewFldSimple().setInstr("PAGE \\* MERGEFORMAT");

2. 目录生成

java 复制代码
// 创建目录段落
XWPFParagraph tocPara = document.createParagraph();
tocPara.setStyle("TOCHeading");

XWPFRun tocRun = tocPara.createRun();
tocRun.setText("目录");
tocRun.setBold(true);
tocRun.setFontSize(16);
tocRun.addBreak();

// 添加目录字段
tocRun = tocPara.createRun();
tocRun.getCTR().addNewFldSimple().setInstr("TOC \\o \"1-3\" \\h \\z \\u");

3. 批注(注释)添加

java 复制代码
// 在第一个段落添加批注
XWPFParagraph firstPara = document.getParagraphs().get(0);
String commentText = "这是系统自动添加的批注";
String commentAuthor = "AutoSystem";
CTComment ctComment = document.getDocument().getBody().addNewComment();
ctComment.setAuthor(commentAuthor);
ctComment.setDate(new GregorianCalendar());
ctComment.addNewP().addNewR().addNewT().setStringValue(commentText);

// 将段落与批注关联
CTMarkupRange range = firstPara.getCTP().addNewCommentRangeStart();
range.setId(ctComment.getId());
firstPara.getCTP().addNewCommentRangeEnd().setId(ctComment.getId());
firstPara.getCTP().addNewR().addNewCommentReference().setId(ctComment.getId());

七、性能优化与最佳实践

1. 大文件处理优化

对于大文档,建议使用事件模型(XWPFEventAPI)而非DOM模型:

java 复制代码
import org.apache.poi.xwpf.eventusermodel.*;

public class LargeDocumentReader {
    public static void main(String[] args) throws Exception {
        OPCPackage pkg = OPCPackage.open("large.docx");
        XWPFEventBasedParser parser = new XWPFEventBasedParser();
        parser.parse(pkg, new XWPFVisitor() {
            @Override
            public void visitParagraph(XWPFParagraph paragraph) {
                System.out.println("段落: " + paragraph.getText());
            }
            
            @Override
            public void visitTable(XWPFTable table) {
                System.out.println("发现表格");
            }
        });
        pkg.close();
    }
}

2. 内存管理

  • 及时关闭文档对象和流
  • 对于批量处理,考虑复用XWPFDocument对象
  • 使用try-with-resources确保资源释放

3. 样式重用

java 复制代码
// 创建并重用样式
CTSectPr sectPr = document.getDocument().getBody().addNewSectPr();
CTPageSz pageSz = sectPr.addNewPgSz();
pageSz.setW(BigInteger.valueOf(12240)); // A4宽度
pageSz.setH(BigInteger.valueOf(15840)); // A4高度

// 创建段落样式
CTStyle ctStyle = CTStyle.Factory.newInstance();
ctStyle.setStyleId("MyStyle");
CTPPr pPr = ctStyle.addNewPPr();
pPr.addNewSpacing().setAfter(BigInteger.valueOf(200)); // 段后间距
document.getStyle().addStyle(ctStyle);

// 应用样式
XWPFParagraph para = document.createParagraph();
para.setStyle("MyStyle");

八、常见问题与解决方案

1. 中文乱码问题

确保使用支持中文的字体:

java 复制代码
run.setFontFamily("宋体");

或者在文档级别设置默认字体:

java 复制代码
document.getStyles().setDefaultFonts("宋体", null);

2. 格式不兼容问题

  • 尽量使用.docx格式而非.doc格式
  • 避免使用太新的Office特性
  • 在生成文档后进行兼容性测试

3. 性能问题

  • 对于大文档,分批次处理内容
  • 避免频繁创建/销毁XWPFRun对象
  • 考虑缓存常用样式

九、总结

Apache POI提供了强大而灵活的API来操作Word文档,从简单的文本生成到复杂的格式设置、表格处理和图片插入都能胜任。通过本文的介绍,您应该已经掌握了:

  1. 基本的文档创建与保存
  2. 文本格式设置与段落控制
  3. 表格的创建与样式设置
  4. 图片插入方法
  5. 现有文档的读取与修改
  6. 高级功能如页眉页脚、目录生成
  7. 性能优化技巧

在实际项目中,建议根据具体需求选择合适的API,并注意资源管理和性能优化。对于更复杂的需求,可以结合模板引擎或考虑商业解决方案。

Apache POI的官方文档和社区资源丰富,遇到问题时可以查阅:

相关推荐
想躺在地上晒成地瓜干18 小时前
树莓派超全系列教程文档--(57)如何设置 Apache web 服务器
服务器·apache·树莓派·raspberrypi·树莓派教程
fs哆哆1 天前
在VBA中,提取word表格的文本时,通常有什么干扰符号,需要清除
word
九月十九1 天前
java操作word里的表格
java·word
流形填表1 天前
word表格批量转excel,提取表格数据到excel
word·excel
时序数据说1 天前
时序数据库Apache IoTDB核心技术深度解析
大数据·数据库·开源·apache·时序数据库·iotdb
SlowFeather1 天前
Apache 反向代理Unity服务器
服务器·unity·apache
蚂蚁数据AntData1 天前
品牌形象全面升级|Apache Fory:破界新生,开启高性能序列化新纪元
架构·开源·apache
小汐睡着了1 天前
word嵌入图片显示不全-error记
word
草明2 天前
提取 Word 中图片原始质量
word