Java解析word怎么获取文档中图片位置

小编给大家分享一下Java解析word怎么获取文档中图片位置,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!

10年的卫滨网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。网络营销推广的优势是能够根据用户设备显示端的尺寸不同,自动调整卫滨建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。成都创新互联从事“卫滨网站设计”,“卫滨网站推广”以来,每个客户项目都认真落实执行。

前言(背景介绍):

Apache POI是Apache基金会下一个开源的项目,用来处理office系列的文档,能够创建和解析word、excel、ppt格式的文档。

其中对word文档的处理有两个技术,分别是HWPF(.doc)和XWPF(.docx)。如果你对这两个技术熟悉的话,就应该能明白使用java解析word文档的痛楚所在。

其中两个最大的问题在于:

第一是这两个类并没有统一的父类和接口(隔壁的XSSF和HSSF投过来鄙视的眼光),所以没法进行同一格式的接口式编程;

第二是官方API中并没有文档中图片相对位置的接口,这就导致了虽然你能获得文档中的所有图片,但是你并不能知道这些图片是在哪里,将来要展示图片就没法插入到正确的位置。

对于第一点,我是没什么办法,可以研究下其他相关技术,比如jacob,doc4j等看看有没有其他的解决方案,不过doc4j这货貌似只能处理2007文档(.docx)。

对于第二点,本文将给出笔者的解决方案,实际上,这也是我写本文的目的所在。

注意:简单求快的同学看第二章和第三章就行了;

一、预备知识

1.word文档的两种格式对应两种不同的存储方式

众所周知,word文档有两种存储格式:doc和docx

doc:习惯上称为Word2003,使用二进制储存数据;这个不是我们今天讨论的重点.

docx:word2007,使用xml来存储数据和格式.

可能你会问了,明明是docx结尾的文档,怎么成了xml格式了?

很简单:你随便选择一个docx文件,右键使用压缩工具打开,就能得到一个这样的目录结构:

Java解析word怎么获取文档中图片位置

所以你以为docx是一个完整的文档,其实它只是一个压缩文件。(docx:?_?)

2.Word文档中xml的定义格式:

从前面我们知道了docx文档使用压缩文件也就是xml来描述数据,那么word文档中的数据具体是怎么定义的呢?

出于篇幅的关系,这里不会详细地描述整个压缩的文档,这里只简单介绍下两个文件/文件夹:

一是word目录下的documen.xml文件,这个就是整个文档内容的定义;

二是word目录下的media文件夹,看名字也能猜出来这个文件夹里面是文档中的多媒体内容:

Java解析word怎么获取文档中图片位置Java解析word怎么获取文档中图片位置

图3:word/document.xml(定义文档内容)                                 

图4:word/media文件夹下的内容

以下是document.xml文档的部分关键内容:

A:document整体结构定义:


 
 
 
 
 
 
 
 
 
 
 
 
 
 
  
  
  
  
  
  
  
  
 

B:文档段落内容:


 
 
 
 
 
 
 
 
 
 
 
 
  
  
  
  
  
  
  
  
 
 
 
 
 
 
 
 
  
  
  
  
  
  
  
  
  
  
  
  
  
  
 
 
 
 
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
 
 
  作者: Brian Dear
 
 
 

C:图片内容定义:


 
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
 
 
  
  
  
  
  
  
  
  
  
  
  
  
  
  
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
  
  
  
  
 
 

有兴趣的童鞋可以看一下上面三段xml代码,我这里直接给结论了:

word文档shema文件:xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"

文档根节点: 定义了整个文档的开始

是document的子节点,文档的主体内容

body子节点,一个段落,就是word文档中的段落

P元素的子节点,一个Run定义了段落中具有相同格式的一段内容

Run元素节点的子节点,就是文档的内容.

run元素的子节点,定义了一张图片:

drawing子节点,具体应用也没有深入研究

定义图片内容

这个是graphic文档的子节点,定义了图片内容的索引,具体来说,poi能根据这个名称拿到图片所对应的资源,而获取文档图片位置的关键也就在这里

Java解析word怎么获取文档中图片位置

总体看来:XWPF解析docx文档就是做了xml文档的解析,将所有的节点保存下来,然后转换成更加好用的属性,提供API出来供用户使用.

所以我们就能用POI提供给我们的接口拿到文档内容,自己去解析文档中的数据,就能获取到图片是在哪一个段落里了,当然你也可以得知图片是位于哪一个Run元素的后面.

二、实现

package com.szdfhx.reportStatistic.util;
import com.microsoft.schemas.vml.CTShape;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFPictureData;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject;
import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class XWPFUtils {
 //获取某一个段落中的所有图片索引
 public static List readImageInParagraph(XWPFParagraph paragraph) {
 //图片索引List
 List imageBundleList = new ArrayList();
 //段落中所有XWPFRun
 List runList = paragraph.getRuns();
 for (XWPFRun run : runList) {
 //XWPFRun是POI对xml元素解析后生成的自己的属性,无法通过xml解析,需要先转化成CTR
 CTR ctr = run.getCTR();
 //对子元素进行遍历
 XmlCursor c = ctr.newCursor();
 //这个就是拿到所有的子元素:
 c.selectPath("./*");
 while (c.toNextSelection()) {
 XmlObject o = c.getObject();
 //如果子元素是这样的形式,使用CTDrawing保存图片
 if (o instanceof CTDrawing) {
  CTDrawing drawing = (CTDrawing) o;
  CTInline[] ctInlines = drawing.getInlineArray();
  for (CTInline ctInline : ctInlines) {
  CTGraphicalObject graphic = ctInline.getGraphic();
  //
  XmlCursor cursor = graphic.getGraphicData().newCursor();
  cursor.selectPath("./*");
  while (cursor.toNextSelection()) {
  XmlObject xmlObject = cursor.getObject();
                // 如果子元素是这样的形式
  if (xmlObject instanceof CTPicture) {
  org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture picture = (org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture) xmlObject;
  //拿到元素的属性
  imageBundleList.add(picture.getBlipFill().getBlip().getEmbed());
  }
  }
  }
 }
 //使用CTObject保存图片
          //形式
 if (o instanceof CTObject) {
  CTObject object = (CTObject) o;
  System.out.println(object);
  XmlCursor w = object.newCursor();
  w.selectPath("./*");
  while (w.toNextSelection()) {
  XmlObject xmlObject = w.getObject();
  if (xmlObject instanceof CTShape) {
  CTShape shape = (CTShape) xmlObject;
  imageBundleList.add(shape.getImagedataArray()[0].getId2());
  }
  }
 }
 }
 }
 return imageBundleList;
 }
}

首先要提出来是XWPF对xml元素的封装:

对应XWPFDocument类

对应XWPFRun类

基本上只对应到Run这一层,因为run的子元素有很多,所以没有再往下面的层次封装和定义了,

所以我们使用API只能拿到所有的XWPFRun对象转成它的xml的定义:CTR对象。最后利用CTR去读取和解析的Run元素中的内容,获取图片的索引。

其次要谈的则是整个XML元素的定义:

我们可以看到POI使用的是Apache下的xmlbeans这个技术解析的XML,相关的技术不做深谈,关键要明白两点:

1:xml文档中的所有元素经过xmlbean是封装后都继承了一个XMLObject的接口,所以可以用这个类来接收获取到的子元素;

2:元素遍历是通过XmlCursor来做的,具体获取子元素是根据XmlCursor对象的selectPath属性来控制,当selectPath为"./*"时就定义为遍历子元素;

所以写成了如下的代码:能遍历当前元素的子元素,并且检验子元素的类型:

CTR ctr = run.getCTR();
//对子元素进行遍历
XmlCursor c = ctr.newCursor();
//这个就是拿到所有的子元素:
c.selectPath("./*");
while (c.toNextSelection()) {
 XmlObject o = c.getObject();
//如果子元素是这样的形式,使用CTDrawing保存图片
if (o instanceof CTDrawing) {
CTDrawing drawing = (CTDrawing) o;

最后你可能会有疑问,不是说这个元素定义了一张图片吗?

那么

if (o instanceof CTObject) {
CTObject object = (CTObject) o;
...
}

这个第二个判断条件是用来干嘛的?

聪明的你应该已经猜到了

没错!docx文档中的xml定义图片的方式除了这一种之外,还可以运用元素去定义,

为什么只有这两种?

因为我只使用第一种方式解析,发现有些图片丢失了,于是发现了第二种方式.......也许不止两种?我也不知道,反正对于目前的我来说已经没有问题了.

或许聪明的你在实践中还遇到了更多种情况?

那么运用上面提到的xml解析方式,相信你也能正确读取,得到自己想要的索引值.

再拓宽一点,如果POI还有其他没有提供的API,我们是不是也能通过XML解析的技术自己实现呢?这个就需要我们在实践中去探索了,相信时间会给我们答案

好了,现在我们拿到了索引值,那么如何去拿到图片资源呢?

POI提供了现成的方法:

XWPFDocument类中有getPictureDataByID(String picture);

方法可以拿到XWPFPictrueDate对象,这个就是图片的资源了.

具体的操作可以参阅相关的博文和API,这里就不详细介绍了.

三、测试:

使用Junit4测试的代码:

package com.szdfhx.reportStatistic.util;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFPictureData;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.*;
public class XWPFUtilsTest {
 @Test
 public void readImageInParagraph() throws IOException {
 InputStream in = new FileInputStream("D:\\Document\\我的博客\\Java解析word,获取文档中图片位置\\示例.docx");
 XWPFDocument xwpfDocument = new XWPFDocument(in);
 List paragraphList = xwpfDocument.getParagraphs();
 System.out.println("图片的索引\t|图片名称\t|图片上一段文字的内容\t");
 System.out.pringln("------------------------------------------");
 for(int i = 0;i < paragraphList.size();i++){
 List imageBundleList = XWPFUtils.readImageInParagraph(paragraphList.get(i));
 if(CollectionUtils.isNotEmpty(imageBundleList)){
 for(String pictureId:imageBundleList){
  XWPFPictureData pictureData = xwpfDocument.getPictureDataByID(pictureId);
  String imageName = pictureData.getFileName();
  String lastParagraphText = paragraphList.get(i-1).getParagraphText();
  System.out.println(pictureId +"\t|" + imageName + "\t|" + lastParagraphText);
 }
 }
 }
 }
}

展示结果:

Java解析word怎么获取文档中图片位置

这里使用图片名称指代表明我拿到了对应的资源,实际上 如果你对前文的内容还熟悉的话,会发现图片的名称实际上就是word/media文件夹下的所有图片的全名称。

在对应的XWPFPictureData对象中,图像的二进制数据可以通过getData()属性来拿到,这样你就可以保存到数据库或者是你本地的文件夹中了!

Java可以用来干什么

Java主要应用于:1. web开发;2. Android开发;3. 客户端开发;4. 网页开发;5. 企业级应用开发;6. Java大数据开发;7.游戏开发等。

看完了这篇文章,相信你对“Java解析word怎么获取文档中图片位置”有了一定的了解,如果想了解更多相关知识,欢迎关注创新互联行业资讯频道,感谢各位的阅读!


当前标题:Java解析word怎么获取文档中图片位置
当前路径:http://csdahua.cn/article/pisohe.html
扫二维码与项目经理沟通

我们在微信上24小时期待你的声音

解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流