解决 poi3.8版本在word中插入图片不展示的问题
1. 前言
-
本次使用的是poi3.8版本,引入的依赖如下:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.8</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.8</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>3.8</version> </dependency>
-
word里插入图片的代码如下:
package org.example.example1.poi38; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.util.Units; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * @Description * @Author susu * @Date 2024/3/8 */ public class TestWordImage2NotDealBug { public static void main(String[] args) throws IOException { // 读取本地的一个现有的Word文档,并创建对应的文档对象 String fileInPath = "/Users/susu/file_test/poi3.8-test/test_word_in_1.docx"; String imagePath = "/Users/susu/file_test/poi3.8-test/susu.jpg"; // 图片文件路径 String fileOutPath = "/Users/susu/file_test/poi3.8-test/test_word_out_1.docx"; FileInputStream fileInputStream = new FileInputStream(fileInPath); XWPFDocument document = new XWPFDocument(fileInputStream); try { XWPFParagraph imageParagraph = document.createParagraph(); XWPFRun imageRun = imageParagraph.createRun(); FileInputStream imageStream = new FileInputStream(imagePath); // imageRun.addPicture(imageStream,XWPFDocument.PICTURE_TYPE_PNG,"susu.png,", Units.toEMU(100), Units.toEMU(100)); imageRun.addPicture(imageStream,XWPFDocument.PICTURE_TYPE_JPEG,"susu.jpg,", Units.toEMU(100), Units.toEMU(100)); FileOutputStream fos = new FileOutputStream(fileOutPath); document.write(fos); fos.close(); } catch (InvalidFormatException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
2. 问题描述
- Java使用poi3.8版本在word中插入图片不展示,使用
XWPFDocument
类的方法addPictureData()
插入图片时虽然不报异常,word也能打开生成的文件,但图片却无法显示出来,如果将word文件用解压缩文件打开,也能看到里面包含的图片文件,但在word中无法正常显示。(图片插入成功,有占位就是不展示),效果如下:
3. 解决问题
3.1 方案1——自定义XWPFDocument
3.1.1 解决代码
- 自定义的 XWPFDocument.java代码如下:
package org.example.example1.poi38; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlToken; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline; import java.io.IOException; import java.io.InputStream; /** * @Description 解决 poi3.8 图片bug问题 * @Author susu * @Date 2024/3/8 */ public class MyXWPFDocument extends XWPFDocument { public MyXWPFDocument() { super(); } public MyXWPFDocument(OPCPackage opcPackage) throws IOException { super(opcPackage); } public MyXWPFDocument(InputStream in) throws IOException { super(in); } public void createPicture(String blipId,int id, int width, int height) { final int EMU = 9525; width *= EMU; height *= EMU; CTInline inline = createParagraph().createRun().getCTR().addNewDrawing().addNewInline(); String picXml = "" + "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" + " <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" + " <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" + " <pic:nvPicPr>" + " <pic:cNvPr id=\"" + id + "\" name=\"Generated\"/>" + " <pic:cNvPicPr/>" + " </pic:nvPicPr>" + " <pic:blipFill>" + " <a:blip r:embed=\"" + blipId + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>" + " <a:stretch>" + " <a:fillRect/>" + " </a:stretch>" + " </pic:blipFill>" + " <pic:spPr>" + " <a:xfrm>" + " <a:off x=\"0\" y=\"0\"/>" + " <a:ext cx=\"" + width + "\" cy=\"" + height + "\"/>" + " </a:xfrm>" + " <a:prstGeom prst=\"rect\">" + " <a:avLst/>" + " </a:prstGeom>" + " </pic:spPr>" + " </pic:pic>" + " </a:graphicData>" + "</a:graphic>"; //CTGraphicalObjectData graphicData = inline.addNewGraphic().addNewGraphicData(); XmlToken xmlToken = null; try { xmlToken = XmlToken.Factory.parse(picXml); } catch(XmlException xe) { xe.printStackTrace(); } inline.set(xmlToken); //graphicData.set(xmlToken); inline.setDistT(0); inline.setDistB(0); inline.setDistL(0); inline.setDistR(0); CTPositiveSize2D extent = inline.addNewExtent(); extent.setCx(width); extent.setCy(height); CTNonVisualDrawingProps docPr = inline.addNewDocPr(); docPr.setId(id); docPr.setName("Picture " + id); docPr.setDescr("Generated"); } }
- 测试代码如下:
package org.example.example1.poi38; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.xwpf.usermodel.XWPFDocument; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; /** * @Description * @Author susu * @Date 2024/3/8 */ public class TestWordImage2DealBug { public static void main(String[] args) throws IOException { // 读取本地的一个现有的Word文档,并创建对应的文档对象 String fileInPath = "/Users/susu/file_test/poi3.8-test/test_word_in_1.docx"; FileInputStream fileInputStream = new FileInputStream(fileInPath); // 使用自定义的 MyXWPFDocument MyXWPFDocument document = new MyXWPFDocument(fileInputStream); try { String imagePath = "/Users/susu/file_test/poi3.8-test/susu.jpg"; // 图片文件路径 FileInputStream imageStream = new FileInputStream(imagePath); String picId = document.addPictureData(imageStream, XWPFDocument.PICTURE_TYPE_JPEG); document.createPicture(picId, document.getNextPicNameNumber(XWPFDocument.PICTURE_TYPE_JPEG), 200, 150); String fileOutPath = "/Users/susu/file_test/poi3.8-test/test_word_out_1.docx"; FileOutputStream fos = new FileOutputStream(fileOutPath); document.write(fos); fos.close(); } catch (InvalidFormatException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
- 测试效果如下(可以看到图片已成功插入):
3.1.2 参考
3.2 方案2——使用开源框架xdocreport
-
只需导入 xdocreport 依赖,代码不用改变,原先图片不展示的代码再测试就没问题了。如下:
-
xdocreport
内部依赖了 poi ,如果xdocreport之后,可以不用导入poi,但是如果还想继续poi3.8版本,则需要指定poi版本,继续导入poi3.8,依赖如下:<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.8</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.8</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>3.8</version> </dependency> <!--引入这个之后,poi3.8就没有图片的bug了--> <dependency> <groupId>fr.opensagres.xdocreport</groupId> <artifactId>org.apache.poi.xwpf.converter.pdf</artifactId> <version>1.0.6</version> </dependency>
-
对比一下导入 xdocreport 之后依赖的变化,如下:
4. 其他
- 如果你的项目很老,升级很闹心,那就暂且用上面方法解决吧!
- 当然如果你用的不是不好升级的老系统,可以考虑更高级的poi版本,也可以了解一下 Poi-tl ,poi-tl是一个基于Apache POI的Word模板引擎,也是一个免费开源的Java类库,主要是官网文档真的很友好,很详细,相当不错,官网如下:
https://deepoove.com/poi-tl/#_why_poi_tl.