在使用POI给word插入图片的时候发现,插入图片后,生成的文件打不开,提示格式损坏,查了一下,这是POI自带的bug,真是不想说这是有多操蛋了。
直接查看word中的document.xml文件,和正常的word中的document.xml对比,发现了POI生成的word格式有问题,因此就得从这方面找解决之道,这是一个正常的document.xml的图片部分,如果大家有相同的问题,可以用这个做对比:
<w:p>
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="7620000" cy="1905000"/>
<wp:docPr id="2" name="Picture 2" descr="Generated"/>
<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="2" name="Generated"/>
<pic:cNvPicPr/>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rId2" 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="7620000" cy="1905000"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
</w:r>
</w:p>
现在看解决办法:
package com.railway.schedule.service;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
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.*;
import java.util.List;
public class CustomXWPFDocument extends XWPFDocument {
public CustomXWPFDocument() {
super();
}
public CustomXWPFDocument(OPCPackage opcPackage) throws IOException {
super(opcPackage);
}
public CustomXWPFDocument(InputStream in) throws IOException {
super(in);
}
public void createPicture(String blipId,int id, int width, int height) {
width = width * 1000;
height = height * 1000;
//String blipId = getAllPictures().get(id).getPackageRelationship().getId();
XWPFParagraph paragraph = createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
CTInline inline = paragraph.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");
}
}
我们把插入图片使用的类继承一下,扩展一下,然后把原生使用的方法绕过就好了,请看具体实现:
private CustomXWPFDocument fillSignalImgToWord(Map<String, String> pathMap, CustomXWPFDocument doc) {
String chartSignalAnalysis1 = pathMap.get("chartSignalAnalysis1");
XWPFRun run;
if (!"".equals(chartSignalAnalysis1) && !"".equals(chartSignalAnalysis2)) {
try {
XWPFParagraph paragraph5 = doc.createParagraph();
run = paragraph5.createRun();
run = Utils.chgFont(run, "宋体", 14);
run.setText(" MT1呼叫信令:");
FileInputStream is = new FileInputStream(chartSignalAnalysis1);
//run.addPicture(is, XWPFDocument.PICTURE_TYPE_PNG, chartSignalAnalysis1, 600, 1200);
doc.createPicture(doc.addPictureData(is, XWPFDocument.PICTURE_TYPE_PNG), doc.getNextPicNameNumber(XWPFDocument.PICTURE_TYPE_PNG), 7000, 8000);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InvalidFormatException e) {
e.printStackTrace();
}
}
return doc;
}
注释掉的代码就是原来插入图片的方法,插入图片后,返回CustomXWPFDocument这个对象,再做其他使用,就OK了
亲测可用!!!