国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 開(kāi)發(fā) > Java > 正文

Java解析word,獲取文檔中圖片位置的方法

2024-07-13 10:15:55
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

前言(背景介紹):

Apache POI是Apache基金會(huì)下一個(gè)開(kāi)源的項(xiàng)目,用來(lái)處理office系列的文檔,能夠創(chuàng)建和解析word、excel、ppt格式的文檔。

其中對(duì)word文檔的處理有兩個(gè)技術(shù),分別是HWPF(.doc)和XWPF(.docx)。如果你對(duì)這兩個(gè)技術(shù)熟悉的話,就應(yīng)該能明白使用java解析word文檔的痛楚所在。

其中兩個(gè)最大的問(wèn)題在于:

第一是這兩個(gè)類(lèi)并沒(méi)有統(tǒng)一的父類(lèi)和接口(隔壁的XSSF和HSSF投過(guò)來(lái)鄙視的眼光),所以沒(méi)法進(jìn)行同一格式的接口式編程;

第二是官方API中并沒(méi)有文檔中圖片相對(duì)位置的接口,這就導(dǎo)致了雖然你能獲得文檔中的所有圖片,但是你并不能知道這些圖片是在哪里,將來(lái)要展示圖片就沒(méi)法插入到正確的位置。

對(duì)于第一點(diǎn),我是沒(méi)什么辦法,可以研究下其他相關(guān)技術(shù),比如jacob,doc4j等看看有沒(méi)有其他的解決方案,不過(guò)doc4j這貨貌似只能處理2007文檔(.docx)。

對(duì)于第二點(diǎn),本文將給出筆者的解決方案,實(shí)際上,這也是我寫(xiě)本文的目的所在。

注意:簡(jiǎn)單求快的同學(xué)看第二章和第三章就行了;

一、預(yù)備知識(shí)

1.word文檔的兩種格式對(duì)應(yīng)兩種不同的存儲(chǔ)方式

眾所周知,word文檔有兩種存儲(chǔ)格式:doc和docx

doc:習(xí)慣上稱為Word2003,使用二進(jìn)制儲(chǔ)存數(shù)據(jù);這個(gè)不是我們今天討論的重點(diǎn).

docx:word2007,使用xml來(lái)存儲(chǔ)數(shù)據(jù)和格式.

可能你會(huì)問(wèn)了,明明是docx結(jié)尾的文檔,怎么成了xml格式了?

很簡(jiǎn)單:你隨便選擇一個(gè)docx文件,右鍵使用壓縮工具打開(kāi),就能得到一個(gè)這樣的目錄結(jié)構(gòu):

 

Java,word,文檔,圖片,位置

所以你以為docx是一個(gè)完整的文檔,其實(shí)它只是一個(gè)壓縮文件。(docx:?_?)

2.Word文檔中xml的定義格式:

從前面我們知道了docx文檔使用壓縮文件也就是xml來(lái)描述數(shù)據(jù),那么word文檔中的數(shù)據(jù)具體是怎么定義的呢?

出于篇幅的關(guān)系,這里不會(huì)詳細(xì)地描述整個(gè)壓縮的文檔,這里只簡(jiǎn)單介紹下兩個(gè)文件/文件夾:

一是word目錄下的documen.xml文件,這個(gè)就是整個(gè)文檔內(nèi)容的定義;

二是word目錄下的media文件夾,看名字也能猜出來(lái)這個(gè)文件夾里面是文檔中的多媒體內(nèi)容:

Java,word,文檔,圖片,位置Java,word,文檔,圖片,位置

圖3:word/document.xml(定義文檔內(nèi)容)                                   

圖4:word/media文件夾下的內(nèi)容

 

以下是document.xml文檔的部分關(guān)鍵內(nèi)容:

A:document整體結(jié)構(gòu)定義:

<w:document mc:ignorable="w14 w15 wp14" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" xmlns:wpscustomdata="http://www.wps.cn/officeDocument/2013/wpsCustomData"> <w:body> <w:p> <w:ppr> <w:pstyle w:val="2"> </w:pstyle> <w:keepnext w:val="0"> </w:keepnext> <w:keeplines w:val="0"> </w:keeplines> <w:widowcontrol> </w:widowcontrol> <w:suppresslinenumbers w:val="0"> </w:suppresslinenumbers> <w:pbdr>  <w:top w:color="auto" w:space="0" w:sz="0" w:val="none">  </w:top>  <w:left w:color="auto" w:space="0" w:sz="0" w:val="none">  </w:left>  <w:bottom w:color="auto" w:space="0" w:sz="0" w:val="none">  </w:bottom>  <w:right w:color="auto" w:space="0" w:sz="0" w:val="none">  </w:right> </w:pbdr>

B:文檔段落內(nèi)容:

<w:p> <w:ppr> <w:pstyle w:val="2"> </w:pstyle> <w:keepnext w:val="0"> </w:keepnext> <w:keeplines w:val="0"> </w:keeplines> <w:widowcontrol> </w:widowcontrol> <w:suppresslinenumbers w:val="0"> </w:suppresslinenumbers> <w:pbdr>  <w:top w:color="auto" w:space="0" w:sz="0" w:val="none">  </w:top>  <w:left w:color="auto" w:space="0" w:sz="0" w:val="none">  </w:left>  <w:bottom w:color="auto" w:space="0" w:sz="0" w:val="none">  </w:bottom>  <w:right w:color="auto" w:space="0" w:sz="0" w:val="none">  </w:right> </w:pbdr> <w:shd w:fill="FAFAFA" w:val="clear"> </w:shd> <w:spacing w:after="150" w:afterautospacing="0" w:before="150" w:beforeautospacing="0" w:line="378" w:linerule="atLeast"> </w:spacing> <w:ind w:firstline="0" w:left="0" w:right="0"> </w:ind> <w:rpr>  <w:rfonts w:ascii="Verdana" w:cs="Verdana" w:hansi="Verdana" w:hint="default">  </w:rfonts>  <w:i w:val="0">  </w:i>  <w:caps w:val="0">  </w:caps>  <w:color w:val="404040">  </w:color>  <w:spacing w:val="0">  </w:spacing>  <w:sz w:val="21">  </w:sz>  <w:szcs w:val="21">  </w:szcs> </w:rpr> </w:ppr> <w:r> <w:rpr>  <w:rfonts w:ascii="Verdana" w:cs="Verdana" w:hansi="Verdana" w:hint="default">  </w:rfonts>  <w:i w:val="0">  </w:i>  <w:caps w:val="0">  </w:caps>  <w:color w:val="404040">  </w:color>  <w:spacing w:val="0">  </w:spacing>  <w:sz w:val="21">  </w:sz>  <w:szcs w:val="21">  </w:szcs>  <w:bdr w:color="auto" w:space="0" w:sz="0" w:val="none">  </w:bdr>  <w:shd w:fill="FAFAFA" w:val="clear">  </w:shd> </w:rpr> <w:t>  作者: Brian Dear </w:t> </w:r> </w:p>

C:圖片內(nèi)容定義:

<w:r> <w:rpr>  <w:rfonts w:ascii="Verdana" w:cs="Verdana" w:hansi="Verdana" w:hint="default">  </w:rfonts>  <w:i w:val="0">  </w:i>  <w:caps w:val="0">  </w:caps>  <w:color w:val="404040">  </w:color>  <w:spacing w:val="0">  </w:spacing>  <w:sz w:val="21">  </w:sz>  <w:szcs w:val="21">  </w:szcs>  <w:bdr w:color="auto" w:space="0" w:sz="0" w:val="none">  </w:bdr>  <w:shd w:fill="FAFAFA" w:val="clear">  </w:shd> </w:rpr> <w:drawing>  <wp:inline distb="0" distl="114300" distr="114300" distt="0">  <wp:extent cx="5543550" cy="5543550">  </wp:extent>  <wp:effectextent b="0" l="0" r="0" t="0">  </wp:effectextent>  <wp:docpr descr="IMG_256" id="1" name="Picture 1">  </wp:docpr>  <wp:cnvgraphicframepr>  <a:graphicframelocks nochangeaspect="1" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">  </a:graphicframelocks>  </wp:cnvgraphicframepr>  <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 descr="IMG_256" id="1" name="Picture 1">   </pic:cnvpr>   <pic:cnvpicpr>   <a:piclocks nochangeaspect="1">   </a:piclocks>   </pic:cnvpicpr>   </pic:nvpicpr>   <pic:blipfill>   <a:blip r:embed="rId4">   </a:blip>   <a:stretch>   <a:fillrect>   </a:fillrect>   </a:stretch>   </pic:blipfill>   <pic:sppr>   <a:xfrm>   <a:off x="0" y="0">   </a:off>   <a:ext cx="5543550" cy="5543550">   </a:ext>   </a:xfrm>   <a:prstgeom prst="rect">   <a:avlst>   </a:avlst>   </a:prstgeom>   <a:nofill>   </a:nofill>   <a:ln w="9525">   <a:nofill>   </a:nofill>   </a:ln>   </pic:sppr>  </pic:pic>  </a:graphicdata>  </a:graphic>  </wp:inline> </w:drawing> </w:r>

有興趣的童鞋可以看一下上面三段xml代碼,我這里直接給結(jié)論了:

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

文檔根節(jié)點(diǎn):<w:document> 定義了整個(gè)文檔的開(kāi)始

<w:body>是document的子節(jié)點(diǎn),文檔的主體內(nèi)容

<w:p>body子節(jié)點(diǎn),一個(gè)段落,就是word文檔中的段落

<w:r>P元素的子節(jié)點(diǎn),一個(gè)Run定義了段落中具有相同格式的一段內(nèi)容

<w:t>Run元素節(jié)點(diǎn)的子節(jié)點(diǎn),就是文檔的內(nèi)容.

<w:drawing> run元素的子節(jié)點(diǎn),定義了一張圖片:

<w:inline> drawing子節(jié)點(diǎn),具體應(yīng)用也沒(méi)有深入研究

<a:graphic> 定義圖片內(nèi)容

<pic:blipfill>這個(gè)是graphic文檔的子節(jié)點(diǎn),定義了圖片內(nèi)容的索引,具體來(lái)說(shuō),poi能根據(jù)這個(gè)名稱拿到圖片所對(duì)應(yīng)的資源,而獲取文檔圖片位置的關(guān)鍵也就在這里

Java,word,文檔,圖片,位置

總體看來(lái):XWPF解析docx文檔就是做了xml文檔的解析,將所有的節(jié)點(diǎn)保存下來(lái),然后轉(zhuǎn)換成更加好用的屬性,提供API出來(lái)供用戶使用.

所以我們就能用POI提供給我們的接口拿到文檔內(nèi)容,自己去解析文檔中的數(shù)據(jù),就能獲取到圖片是在哪一個(gè)段落里了,當(dāng)然你也可以得知圖片是位于哪一個(gè)Run元素的后面.

二、實(shí)現(xiàn)

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 { //獲取某一個(gè)段落中的所有圖片索引 public static List<String> readImageInParagraph(XWPFParagraph paragraph) { //圖片索引List List<String> imageBundleList = new ArrayList<String>(); //段落中所有XWPFRun List<XWPFRun> runList = paragraph.getRuns(); for (XWPFRun run : runList) { //XWPFRun是POI對(duì)xml元素解析后生成的自己的屬性,無(wú)法通過(guò)xml解析,需要先轉(zhuǎn)化成CTR CTR ctr = run.getCTR(); //對(duì)子元素進(jìn)行遍歷 XmlCursor c = ctr.newCursor(); //這個(gè)就是拿到所有的子元素: c.selectPath("./*"); while (c.toNextSelection()) { XmlObject o = c.getObject(); //如果子元素是<w:drawing>這樣的形式,使用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();                // 如果子元素是<pic:pic>這樣的形式  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保存圖片          //<w:object>形式 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; }}

首先要提出來(lái)是XWPF對(duì)xml元素的封裝:

<w:document> 對(duì)應(yīng)XWPFDocument類(lèi)

<w:run>對(duì)應(yīng)XWPFRun類(lèi)

基本上只對(duì)應(yīng)到Run這一層,因?yàn)閞un的子元素有很多,所以沒(méi)有再往下面的層次封裝和定義了,

所以我們使用API只能拿到所有的XWPFRun對(duì)象轉(zhuǎn)成它的xml的定義:CTR對(duì)象。最后利用CTR去讀取和解析的Run元素中的內(nèi)容,獲取圖片的索引。

其次要談的則是整個(gè)XML元素的定義:

我們可以看到POI使用的是Apache下的xmlbeans這個(gè)技術(shù)解析的XML,相關(guān)的技術(shù)不做深談,關(guān)鍵要明白兩點(diǎn):

1:xml文檔中的所有元素經(jīng)過(guò)xmlbean是封裝后都繼承了一個(gè)XMLObject的接口,所以可以用這個(gè)類(lèi)來(lái)接收獲取到的子元素;

2:元素遍歷是通過(guò)XmlCursor來(lái)做的,具體獲取子元素是根據(jù)XmlCursor對(duì)象的selectPath屬性來(lái)控制,當(dāng)selectPath為"./*"時(shí)就定義為遍歷子元素;

所以寫(xiě)成了如下的代碼:能遍歷當(dāng)前元素的子元素,并且檢驗(yàn)子元素的類(lèi)型:

CTR ctr = run.getCTR();//對(duì)子元素進(jìn)行遍歷XmlCursor c = ctr.newCursor();//這個(gè)就是拿到所有的子元素:c.selectPath("./*");while (c.toNextSelection()) { XmlObject o = c.getObject();//如果子元素是<w:drawing>這樣的形式,使用CTDrawing保存圖片if (o instanceof CTDrawing) {CTDrawing drawing = (CTDrawing) o;

         

最后你可能會(huì)有疑問(wèn),不是說(shuō)<w:drawing>這個(gè)元素定義了一張圖片嗎?

那么

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

這個(gè)第二個(gè)判斷條件是用來(lái)干嘛的?

聰明的你應(yīng)該已經(jīng)猜到了

沒(méi)錯(cuò)!docx文檔中的xml定義圖片的方式除了<w:drawing>這一種之外,還可以運(yùn)用<w:object>元素去定義,

為什么只有這兩種?

因?yàn)槲抑皇褂玫谝环N方式解析,發(fā)現(xiàn)有些圖片丟失了,于是發(fā)現(xiàn)了第二種方式.......也許不止兩種?我也不知道,反正對(duì)于目前的我來(lái)說(shuō)已經(jīng)沒(méi)有問(wèn)題了.

或許聰明的你在實(shí)踐中還遇到了更多種情況?

那么運(yùn)用上面提到的xml解析方式,相信你也能正確讀取,得到自己想要的索引值.

再拓寬一點(diǎn),如果POI還有其他沒(méi)有提供的API,我們是不是也能通過(guò)XML解析的技術(shù)自己實(shí)現(xiàn)呢?這個(gè)就需要我們?cè)趯?shí)踐中去探索了,相信時(shí)間會(huì)給我們答案

好了,現(xiàn)在我們拿到了索引值,那么如何去拿到圖片資源呢?

POI提供了現(xiàn)成的方法:

XWPFDocument類(lèi)中有g(shù)etPictureDataByID(String picture);

方法可以拿到XWPFPictrueDate對(duì)象,這個(gè)就是圖片的資源了.

具體的操作可以參閱相關(guān)的博文和API,這里就不詳細(xì)介紹了.

三、測(cè)試:

使用Junit4測(cè)試的代碼:

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<XWPFParagraph> paragraphList = xwpfDocument.getParagraphs(); System.out.println("圖片的索引/t|圖片名稱/t|圖片上一段文字的內(nèi)容/t"); System.out.pringln("------------------------------------------"); for(int i = 0;i < paragraphList.size();i++){ List<String> 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); } } } }}

展示結(jié)果:

Java,word,文檔,圖片,位置

這里使用圖片名稱指代表明我拿到了對(duì)應(yīng)的資源,實(shí)際上 如果你對(duì)前文的內(nèi)容還熟悉的話,會(huì)發(fā)現(xiàn)圖片的名稱實(shí)際上就是word/media文件夾下的所有圖片的全名稱。

在對(duì)應(yīng)的XWPFPictureData對(duì)象中,圖像的二進(jìn)制數(shù)據(jù)可以通過(guò)getData()屬性來(lái)拿到,這樣你就可以保存到數(shù)據(jù)庫(kù)或者是你本地的文件夾中了!

四、其他:

談到這里,開(kāi)頭提到的第二個(gè)問(wèn)題這里就已經(jīng)解決了。

那么,第一個(gè)問(wèn)題怎么辦呢?

如果你的系統(tǒng)對(duì)速度要求不高的話,那么我給你的建議是,把doc文檔轉(zhuǎn)化成docx文檔來(lái)解析--POI就有成熟的API來(lái)做

如果要考慮性能的話,那就只好寫(xiě)兩套方法去解析文檔。

那么......doc類(lèi)型的word文檔怎么獲取圖片的相對(duì)位置呢?

我也不知道········或者,你來(lái)告訴我?

以上這篇Java解析word,獲取文檔中圖片位置的方法就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持VeVb武林網(wǎng)。


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到JAVA教程頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 桦甸市| 桂林市| 辉南县| 马尔康县| 青岛市| 尼勒克县| 平山县| 平原县| 盐城市| 临洮县| 庆城县| 德安县| 隆尧县| 寿光市| 鄂托克前旗| 保定市| 巴彦淖尔市| 老河口市| 安国市| 香河县| 乌苏市| 马鞍山市| 武汉市| 大荔县| 青州市| 道孚县| 陕西省| 秦皇岛市| 南木林县| 从江县| 上犹县| 唐山市| 长寿区| 福建省| 贵定县| 安新县| 新乐市| 楚雄市| 乃东县| 桂平市| 潜江市|