如何使用Apache POI向现有docx文件插入图片,解决文件无法读取报错
问题原因
- 核心触发原因:若你使用的原始docx文件从未插入过PNG格式图片,Apache POI向现有文档插入PNG图片时,不会自动在
[Content_Types].xml配置文件中添加PNG格式的映射规则,导致Word无法识别插入的图片,判定文件损坏不可读。新建文档时POI会自动初始化所有常用图片格式的映射,所以新建文档插入图片不会报错。 - 代码逻辑问题:你当前将同一段落内所有run的文本拼接后清空全部run,仅在最后一个run输出内容,会丢失原有run的格式(如粗体、颜色、字号等),且插入图片时没有处理[Signature]占位符之外的剩余文本内容。
- IO流管理问题:创建XWPFDocument时使用的文件输入流未显式关闭,可能引发文件资源泄漏。
修复方案
首先手动添加PNG格式映射解决文件损坏问题,再修正替换逻辑、规范流管理即可,修复后代码如下:
// 文档读写入口逻辑,使用try-with-resources自动关闭流 try (FileInputStream fis = new FileInputStream(inputURL); XWPFDocument doc = new XWPFDocument(fis)) { // 手动注册PNG格式映射,解决现有文档无PNG配置的核心问题 OPCPackage opcPackage = doc.getPackage(); if (!opcPackage.getContentTypeManager().isContentTypeRegistered("png")) { opcPackage.getContentTypeManager().addDefaultContentType("png", "image/png"); } replaceFields(doc, replacings); // 输出生成的文档 try (FileOutputStream fos = new FileOutputStream(outputURL)) { doc.write(fos); } } catch (Exception e) { e.printStackTrace(); } public static void replaceFields(XWPFDocument doc, HashMap<String, String> replacings) throws IOException, InvalidFormatException { int pCounter = 0; for (XWPFParagraph p : doc.getParagraphs()) { pCounter++; List<XWPFRun> runs = p.getRuns(); if (runs == null || runs.isEmpty()) continue; // 拼接整段完整文本 StringBuilder fullText = new StringBuilder(); for (XWPFRun run : runs) { fullText.append(run.text()); } String processedText = fullText.toString(); // 执行普通占位符替换 for (String key : replacings.keySet()) { String value = replacings.get(key); if (value.isEmpty()) { processedText = processedText.replace(key + " ", "").replace(key, ""); } else { processedText = processedText.replace(key, value); } } // 清空原有全部run for (int i = runs.size() - 1; i >= 0; i--) { p.removeRun(i); } // 处理签名占位符 if (processedText.contains("[Signature]")) { String[] parts = processedText.split("\\[Signature\\]", 2); // 插入占位符前的文本 if (!parts[0].isEmpty()) { XWPFRun preRun = p.createRun(); preRun.setText(parts[0]); } // 插入签名图片 XWPFRun imgRun = p.createRun(); String signatureFile = "C:\\Users\\rick.van.baalen\\Downloads\\Prodin_Templates\\Signature_RVB.png"; try (InputStream is = new FileInputStream(signatureFile)) { imgRun.addPicture(is, XWPFDocument.PICTURE_TYPE_PNG, signatureFile, Units.toEMU(80), Units.toEMU(65)); } // 插入占位符后的文本 if (parts.length > 1 && !parts[1].isEmpty()) { XWPFRun postRun = p.createRun(); postRun.setText(parts[1]); } } else { // 无签名占位符直接输出替换后的文本 XWPFRun newRun = p.createRun(); newRun.setText(processedText); } System.out.println("Line " + pCounter + ": " + processedText); } }
补充说明
如果使用的Apache POI版本低于4.1.2,建议升级到4.1.2或5.x稳定版本,旧版本存在多个插入图片相关的已知BUG,也可能导致文件损坏。如果需要保留原有段落的格式,可以在创建新run时复制原有run的格式属性即可。
内容的提问来源于stack exchange,提问作者Rick van Baalen
相关产品推荐
相关产品推荐

