0

0

如何在 Apache POI 中精准定位并插入图片到 Word 文档指定文本后

聖光之護

聖光之護

发布时间:2026-01-01 18:32:22

|

542人浏览过

|

来源于php中文网

原创

如何在 Apache POI 中精准定位并插入图片到 Word 文档指定文本后

本文详解使用 apache poi(xwpf)在 ms word 文档中,于每个匹配关键词(如 "word")之后自动插入图片的完整实现方案,重点解决图片不可见、并发修改异常及文本分段导致匹配失败三大核心问题。

在基于 Apache POI 操作 Word(.docx)文档时,许多开发者尝试通过遍历 XWPFRun 并调用 insertNewRun() 插入图片,却发现图片“未显示”或程序抛出 ConcurrentModificationException。根本原因在于:Word 的底层文本结构高度碎片化、图片尺寸单位易被误解、以及动态插入操作破坏迭代器稳定性。以下为专业级解决方案。

Stable Diffusion Online
Stable Diffusion Online

基于Stable Diffusion搭建的AI绘图工具

下载

✅ 核心问题与修复要点

  1. 图片尺寸单位错误(最常见隐形 Bug)
    XWPFRun.addPicture(..., width, height) 的 width/height 参数单位是 EMU(English Metric Units),而非像素(px)或点(pt)。直接传入 200 表示 200 EMU ≈ 0.000556 cm —— 远小于 1 像素,肉眼完全不可见。
    ✅ 正确做法:使用 Units.pixelToEMU(200)(若原始尺寸为像素)或 Units.toEMU(200)(若为点):

    import org.apache.poi.util.Units;
    // ...
    imageRun.addPicture(
        new FileInputStream(imgFile),
        XWPFDocument.PICTURE_TYPE_PNG,
        imgFile,
        Units.pixelToEMU(200),    // ✅ 转换为 EMU
        Units.pixelToEMU(150)     // ✅ 高度同理
    );
  2. 避免 ConcurrentModificationException
    使用增强 for 循环 for (XWPFRun run : runs) 时,若在循环体内调用 paragraph.insertNewRun(),会向 runs 列表插入新元素,导致底层 Iterator 失效。
    ✅ 安全替代:改用索引遍历,并注意 insertNewRun() 后需重新获取 runs 列表(因内部结构已变更):

    List<XWPFRun> runs = paragraph.getRuns();
    for (int r = 0; r < runs.size(); r++) {
        XWPFRun run = runs.get(r);
        String text = run.getText(0);
        if (text != null && text.contains("word")) {
            // ✅ 安全插入:使用当前索引 + 1
            XWPFRun imageRun = paragraph.insertNewRun(r + 1);
            // ⚠️ 注意:此时 runs 已过期,后续需重新 getRuns()
            runs = paragraph.getRuns(); // 重载最新 runs
            // ... 插入图片逻辑
        }
    }
  3. 精准匹配被拆分的关键词(Word 文本分段陷阱)
    Word 可能将一个单词 "word" 拆分为多个 XWPFRun(如 word),导致 run.getText(0).contains("word") 永远为 false。
    ✅ 推荐方案:使用 XWPFParagraph.searchText() 配合 TextSegment,它能跨 Run 级别进行语义搜索:

    PositionInParagraph startPos = new PositionInParagraph(0, 0, 0);
    TextSegment segment;
    while ((segment = paragraph.searchText("word", startPos)) != null) {
        int endRunIndex = segment.getEndRun();
        List<XWPFRun> runs = paragraph.getRuns();
        XWPFRun targetRun = runs.get(endRunIndex);
    
        // 在目标 Run 后插入新 Run(即关键词后)
        XWPFRun imageRun = paragraph.insertNewRun(endRunIndex + 1);
        // ... 添加图片
    
        // 更新搜索起始位置:跳过刚插入的图片 Run
        startPos = new PositionInParagraph(
            runs.indexOf(imageRun),  // 新 Run 的索引
            0, 0                      // 从该 Run 开头继续
        );
    }

✅ 完整可运行示例(推荐使用)

import java.io.*;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.util.Units;
import java.util.List;

public class ImageWord {
    public static void main(String[] args) throws Exception {
        // ✅ 使用 .docx(非 .doc)格式;路径建议用相对路径或 Path.of()
        try (XWPFDocument doc = new XWPFDocument(new FileInputStream("./image.docx"))) {
            List<XWPFParagraph> paragraphs = doc.getParagraphs();
            File[] images = new File("./images/").listFiles((dir, name) -> 
                name.toLowerCase().endsWith(".png") || 
                name.toLowerCase().endsWith(".jpg"));

            if (images == null || images.length == 0) {
                throw new RuntimeException("No images found in ./images/");
            }

            int imageCounter = 0;
            String keyword = "word";

            for (XWPFParagraph paragraph : paragraphs) {
                PositionInParagraph pos = new PositionInParagraph(0, 0, 0);
                TextSegment segment;

                while ((segment = paragraph.searchText(keyword, pos)) != null) {
                    List<XWPFRun> runs = paragraph.getRuns();
                    int insertIndex = segment.getEndRun() + 1;

                    // 创建新 Run 并插入图片
                    XWPFRun imageRun = paragraph.insertNewRun(insertIndex);
                    File imgFile = images[imageCounter % images.length];

                    try (FileInputStream fis = new FileInputStream(imgFile)) {
                        imageRun.addPicture(
                            fis,
                            XWPFDocument.PICTURE_TYPE_PNG,
                            imgFile.getName(),
                            Units.pixelToEMU(200),
                            Units.pixelToEMU(150)
                        );
                    }
                    imageRun.addBreak(); // 可选:图片后换行

                    // 更新搜索位置,避免重复匹配或越界
                    runs = paragraph.getRuns();
                    int newIndex = runs.indexOf(imageRun);
                    pos = new PositionInParagraph(newIndex, 0, 0);
                    imageCounter++;
                }
            }

            // ✅ 使用 try-with-resources 或显式 close
            try (FileOutputStream out = new FileOutputStream("./images_output.docx")) {
                doc.write(out);
            }
        }
    }
}

⚠️ 注意事项与最佳实践

  • 文件格式:确保输入为 .docx(OOXML 格式),Apache POI 对旧版 .doc 支持有限且不稳定。
  • 依赖版本:使用 Apache POI ≥ 5.2.4,以获得完整的 TextSegment 和 searchText 支持。
  • 图片路径:addPicture 的第三个参数(pictureName)建议传入文件名(如 imgFile.getName()),而非绝对路径,利于文档可移植性。
  • 异常处理:务必包裹 FileInputStream 在 try-with-resources 中,防止资源泄漏;对 images 数组判空,避免 NullPointerException。
  • 性能提示:若文档极大或图片极多,考虑批量写入或启用 XWPFDocument 的 setCompressPictures(true)。

通过以上三重加固(EMU 单位转换、安全索引遍历、TextSegment 语义搜索),即可稳定、精准地在任意 Word 文档关键词后插入图片,真正实现自动化文档生成场景下的生产级需求。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
apache是什么意思
apache是什么意思

Apache是Apache HTTP Server的简称,是一个开源的Web服务器软件。是目前全球使用最广泛的Web服务器软件之一,由Apache软件基金会开发和维护,Apache具有稳定、安全和高性能的特点,得益于其成熟的开发和广泛的应用实践,被广泛用于托管网站、搭建Web应用程序、构建Web服务和代理等场景。本专题为大家提供了Apache相关的各种文章、以及下载和课程,希望对各位有所帮助。

418

2023.08.23

apache启动失败
apache启动失败

Apache启动失败可能有多种原因。需要检查日志文件、检查配置文件等等。想了解更多apache启动的相关内容,可以阅读本专题下面的文章。

937

2024.01.16

Java 流式处理与 Apache Kafka 实战
Java 流式处理与 Apache Kafka 实战

本专题专注讲解 Java 在流式数据处理与消息队列系统中的应用,系统讲解 Apache Kafka 的基础概念、生产者与消费者模型、Kafka Streams 与 KSQL 流式处理框架、实时数据分析与监控,结合实际业务场景,帮助开发者构建 高吞吐量、低延迟的实时数据流管道,实现高效的数据流转与处理。

119

2026.02.04

PHP 命令行脚本与自动化任务开发
PHP 命令行脚本与自动化任务开发

本专题系统讲解 PHP 在命令行环境(CLI)下的开发与应用,内容涵盖 PHP CLI 基础、参数解析、文件与目录操作、日志输出、异常处理,以及与 Linux 定时任务(Cron)的结合使用。通过实战示例,帮助开发者掌握使用 PHP 构建 自动化脚本、批处理工具与后台任务程序 的能力。

62

2025.12.13

word背景色怎么改成白色
word背景色怎么改成白色

Word是微软公司的一个文字处理器软件。word为用户提供了专业而优雅的文档工具,帮助用户节省时间并得到优雅美观的结果。word提供了许多易于使用的文档创建工具,同时也提供了丰富的功能供创建复杂的文档使用。怎么word背景色怎么该呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

3736

2023.07.21

word最后一页空白页怎么删除
word最后一页空白页怎么删除

word最后一页空白页删除方法有:通过删除回车符、调整页边距、删除分节符或调整分页符位置,您可以轻松去除最后一页的空白页。根据您实际的文档情况,选择适合您的方法进行操作,使您的文档更加美观和整洁。本专题为大家提供word最后一页空白页怎么删除不了相关的各种文章、以及下载和课程。

337

2023.07.24

word最后一页空白页怎么删除不了
word最后一页空白页怎么删除不了

word删除最后一页空白页,可以尝试使用Backspace键删除空白页,如果无效,查找和删除分页符,或者调整页面边距和行距。还可以尝试将文档保存为其他格式并重新打开和保存。本专题为大家提供word最后一页空白页为啥删除不了的相关的文章、下载、课程内容,供大家免费下载体验。

375

2023.07.25

word单页改变纸张方向
word单页改变纸张方向

word单页改变纸张方向:1、在界面上选择文档纸张方向;2、自定义页面设置;3、分节功能。本专题为大家提供word单页改变纸张方向的相关的文章、下载、课程内容,供大家免费下载体验。

624

2023.07.27

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

6

2026.02.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4万人学习

C# 教程
C# 教程

共94课时 | 10.4万人学习

Java 教程
Java 教程

共578课时 | 73.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号