1. 简介
在日常办公自动化开发中,常常需要对 Word 文档中的图片进行批量提取、保存,甚至将图片替换为自定义的文本或链接。Apache POI 是一款强大的 Java 开源库,支持对 Microsoft Office 文档(包括 Word、Excel、PowerPoint 等)进行读写操作。本文将介绍如何使用 POI 提取 Word 文档中的图片,并将图片在文档中替换为自定义文本。
2. 环境准备
- JDK 1.8+
- Maven/Gradle(推荐使用 Maven 管理依赖)
- POI 依赖
Maven 依赖示例:
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version>
</dependency>
3. 提取图片
3.1 基本思路
- 使用 XWPFDocument 加载 Word 文档(.docx)。
- 通过 getAllPictures() 方法获取文档中所有图片数据。
- 将图片数据保存到本地磁盘。
3.2 示例代码
import org.apache.poi.xwpf.usermodel.*;
import java.io.*;
import java.nio.file.*;
import java.util.List;public class WordImageExtractor {public static void extractImages(String docxPath, String outputDir) throws Exception {try (FileInputStream fis = new FileInputStream(docxPath);XWPFDocument document = new XWPFDocument(fis)) {List<XWPFPictureData> pictures = document.getAllPictures();int index = 1;for (XWPFPictureData picture : pictures) {String ext = picture.suggestFileExtension();byte[] data = picture.getData();String fileName = "image_" + index++ + "." + ext;Path imagePath = Paths.get(outputDir, fileName);Files.write(imagePath, data);System.out.println("图片已保存: " + imagePath);}}}
}
4. 替换图片为自定义文本
4.1 基本思路
- 遍历文档的所有段落和 run。
- 检查 run 是否包含图片(getEmbeddedPictures())。
- 删除包含图片的 run,并插入新的 run,写入自定义文本(如图片路径或占位符)。
4.2 示例代码
public static void replaceImagesWithText(String docxPath, String outputPath, String replacementText) throws Exception {try (FileInputStream fis = new FileInputStream(docxPath);XWPFDocument document = new XWPFDocument(fis)) {for (XWPFParagraph paragraph : document.getParagraphs()) {List<XWPFRun> runs = paragraph.getRuns();for (int i = runs.size() - 1; i >= 0; i--) {XWPFRun run = runs.get(i);if (!run.getEmbeddedPictures().isEmpty()) {paragraph.removeRun(i);XWPFRun newRun = paragraph.insertNewRun(i);newRun.setText(replacementText);}}}try (FileOutputStream fos = new FileOutputStream(outputPath)) {document.write(fos);}System.out.println("图片已替换,文档保存为: " + outputPath);}
}
5. 综合示例:提取并替换
将图片提取到本地后,用图片的本地路径替换文档中的图片:
//找到拆分文档中的图片 并进行占位符的替换InputStream docStream = new FileInputStream(fullPath);int index = 0;String newFileName = "processed_" + fileName;String outputPath = outputDir + "/" + newFileName;try (XWPFDocument document = new XWPFDocument(docStream)) {List<XWPFPictureData> pictures = document.getAllPictures();System.out.println("提取到的图片数量: " + pictures.size());// 保存图片并记录图片信息Map<String, String> picturePathMap = new HashMap<>();for (XWPFPictureData pictureData : pictures) {try {byte[] data = pictureData.getData();BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(data));if (originalImage != null) {// 获取图片格式String fileExtension = pictureData.suggestFileExtension();// 生成保存路径String outputPath = String.format("%s/image_%d_%s.%s",outputDir,++index,fileName,fileExtension);// 保存图片File outputFile = new File(outputPath);// 如果ImageIO不支持该格式,直接保存原始字节数据if (!outputFile.exists() || outputFile.length() == 0) {Files.write(Paths.get(outputPath), data);// 记录图片ID和路径的映射picturePathMap.put(pictureData.getPackagePart().getPartName().getName(),outputPath);}System.out.println(String.format("已保存第 %d 张图片: %s", index, outputPath));}} catch (Exception e) {System.err.println("处理第 " + (index + 1) + " 张图片时出错: " + e.getMessage());}}// 处理文档中的段落,替换图片为文本for (XWPFParagraph paragraph : document.getParagraphs()) {List<XWPFRun> runs = paragraph.getRuns();for (int k = runs.size() - 1; k >= 0; k--) {XWPFRun run = runs.get(k);List<XWPFPicture> pictures2 = run.getEmbeddedPictures();if (!pictures2.isEmpty()) {// 获取图片路径String picturePath = picturePathMap.get(pictures2.get(0).getPictureData().getPackagePart().getPartName().getName());if (picturePath != null) {// 删除当前run中的内容paragraph.removeRun(k);// 创建新的run并添加文本XWPFRun newRun = paragraph.insertNewRun(k);newRun.setText("[图片地址: " + picturePath + "]");}}}}// 保存修改后的文档try (FileOutputStream fos = new FileOutputStream(outputPath)) {document.write(fos);}FileUtil.del(fullPath);System.out.println("处理完成,新文档保存为: " + outputPath);}
6. 注意事项
- 只支持 .docx 格式(.doc 需用 HWPF,API 不同)。
- 图片提取顺序与文档中出现顺序一致,但图片与 run 的映射需通过 getPackagePart().getPartName().getName() 匹配。
- 替换时建议从后往前遍历 run,避免索引错乱。
7. 总结
通过 Apache POI,可以方便地实现 Word 文档图片的批量提取和替换。实际开发中可根据业务需求,灵活调整图片保存路径、替换文本等逻辑。