小編給大家分享一下Java解析word怎么獲取文檔中圖片位置,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
10年的衛(wèi)濱網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。網(wǎng)絡(luò)營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整衛(wèi)濱建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。成都創(chuàng)新互聯(lián)從事“衛(wèi)濱網(wǎng)站設(shè)計”,“衛(wèi)濱網(wǎng)站推廣”以來,每個客戶項目都認(rèn)真落實執(zhí)行。
前言(背景介紹):
Apache POI是Apache基金會下一個開源的項目,用來處理office系列的文檔,能夠創(chuàng)建和解析word、excel、ppt格式的文檔。
其中對word文檔的處理有兩個技術(shù),分別是HWPF(.doc)和XWPF(.docx)。如果你對這兩個技術(shù)熟悉的話,就應(yīng)該能明白使用java解析word文檔的痛楚所在。
其中兩個最大的問題在于:
第一是這兩個類并沒有統(tǒng)一的父類和接口(隔壁的XSSF和HSSF投過來鄙視的眼光),所以沒法進行同一格式的接口式編程;
第二是官方API中并沒有文檔中圖片相對位置的接口,這就導(dǎo)致了雖然你能獲得文檔中的所有圖片,但是你并不能知道這些圖片是在哪里,將來要展示圖片就沒法插入到正確的位置。
對于第一點,我是沒什么辦法,可以研究下其他相關(guān)技術(shù),比如jacob,doc4j等看看有沒有其他的解決方案,不過doc4j這貨貌似只能處理2007文檔(.docx)。
對于第二點,本文將給出筆者的解決方案,實際上,這也是我寫本文的目的所在。
注意:簡單求快的同學(xué)看第二章和第三章就行了;
一、預(yù)備知識
1.word文檔的兩種格式對應(yīng)兩種不同的存儲方式
眾所周知,word文檔有兩種存儲格式:doc和docx
doc:習(xí)慣上稱為Word2003,使用二進制儲存數(shù)據(jù);這個不是我們今天討論的重點.
docx:word2007,使用xml來存儲數(shù)據(jù)和格式.
可能你會問了,明明是docx結(jié)尾的文檔,怎么成了xml格式了?
很簡單:你隨便選擇一個docx文件,右鍵使用壓縮工具打開,就能得到一個這樣的目錄結(jié)構(gòu):
所以你以為docx是一個完整的文檔,其實它只是一個壓縮文件。(docx:?_?)
2.Word文檔中xml的定義格式:
從前面我們知道了docx文檔使用壓縮文件也就是xml來描述數(shù)據(jù),那么word文檔中的數(shù)據(jù)具體是怎么定義的呢?
出于篇幅的關(guān)系,這里不會詳細地描述整個壓縮的文檔,這里只簡單介紹下兩個文件/文件夾:
一是word目錄下的documen.xml文件,這個就是整個文檔內(nèi)容的定義;
二是word目錄下的media文件夾,看名字也能猜出來這個文件夾里面是文檔中的多媒體內(nèi)容:
圖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é)點:<w:document> 定義了整個文檔的開始
<w:body>是document的子節(jié)點,文檔的主體內(nèi)容
<w:p>body子節(jié)點,一個段落,就是word文檔中的段落
<w:r>P元素的子節(jié)點,一個Run定義了段落中具有相同格式的一段內(nèi)容
<w:t>Run元素節(jié)點的子節(jié)點,就是文檔的內(nèi)容.
<w:drawing> run元素的子節(jié)點,定義了一張圖片:
<w:inline> drawing子節(jié)點,具體應(yīng)用也沒有深入研究
<a:graphic> 定義圖片內(nèi)容
<pic:blipfill>這個是graphic文檔的子節(jié)點,定義了圖片內(nèi)容的索引,具體來說,poi能根據(jù)這個名稱拿到圖片所對應(yīng)的資源,而獲取文檔圖片位置的關(guān)鍵也就在這里
總體看來:XWPF解析docx文檔就是做了xml文檔的解析,將所有的節(jié)點保存下來,然后轉(zhuǎn)換成更加好用的屬性,提供API出來供用戶使用.
所以我們就能用POI提供給我們的接口拿到文檔內(nèi)容,自己去解析文檔中的數(shù)據(jù),就能獲取到圖片是在哪一個段落里了,當(dāng)然你也可以得知圖片是位于哪一個Run元素的后面.
二、實現(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 { //獲取某一個段落中的所有圖片索引 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對xml元素解析后生成的自己的屬性,無法通過xml解析,需要先轉(zhuǎn)化成CTR CTR ctr = run.getCTR(); //對子元素進行遍歷 XmlCursor c = ctr.newCursor(); //這個就是拿到所有的子元素: 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; } }
首先要提出來是XWPF對xml元素的封裝:
<w:document> 對應(yīng)XWPFDocument類
<w:run>對應(yīng)XWPFRun類
基本上只對應(yīng)到Run這一層,因為run的子元素有很多,所以沒有再往下面的層次封裝和定義了,
所以我們使用API只能拿到所有的XWPFRun對象轉(zhuǎn)成它的xml的定義:CTR對象。最后利用CTR去讀取和解析的Run元素中的內(nèi)容,獲取圖片的索引。
其次要談的則是整個XML元素的定義:
我們可以看到POI使用的是Apache下的xmlbeans這個技術(shù)解析的XML,相關(guān)的技術(shù)不做深談,關(guān)鍵要明白兩點:
1:xml文檔中的所有元素經(jīng)過xmlbean是封裝后都繼承了一個XMLObject的接口,所以可以用這個類來接收獲取到的子元素;
2:元素遍歷是通過XmlCursor來做的,具體獲取子元素是根據(jù)XmlCursor對象的selectPath屬性來控制,當(dāng)selectPath為"./*"時就定義為遍歷子元素;
所以寫成了如下的代碼:能遍歷當(dāng)前元素的子元素,并且檢驗子元素的類型:
CTR ctr = run.getCTR(); //對子元素進行遍歷 XmlCursor c = ctr.newCursor(); //這個就是拿到所有的子元素: c.selectPath("./*"); while (c.toNextSelection()) { XmlObject o = c.getObject(); //如果子元素是<w:drawing>這樣的形式,使用CTDrawing保存圖片 if (o instanceof CTDrawing) { CTDrawing drawing = (CTDrawing) o;
最后你可能會有疑問,不是說<w:drawing>這個元素定義了一張圖片嗎?
那么
if (o instanceof CTObject) { CTObject object = (CTObject) o; ... }
這個第二個判斷條件是用來干嘛的?
聰明的你應(yīng)該已經(jīng)猜到了
沒錯!docx文檔中的xml定義圖片的方式除了<w:drawing>這一種之外,還可以運用<w:object>元素去定義,
為什么只有這兩種?
因為我只使用第一種方式解析,發(fā)現(xiàn)有些圖片丟失了,于是發(fā)現(xiàn)了第二種方式.......也許不止兩種?我也不知道,反正對于目前的我來說已經(jīng)沒有問題了.
或許聰明的你在實踐中還遇到了更多種情況?
那么運用上面提到的xml解析方式,相信你也能正確讀取,得到自己想要的索引值.
再拓寬一點,如果POI還有其他沒有提供的API,我們是不是也能通過XML解析的技術(shù)自己實現(xiàn)呢?這個就需要我們在實踐中去探索了,相信時間會給我們答案
好了,現(xiàn)在我們拿到了索引值,那么如何去拿到圖片資源呢?
POI提供了現(xiàn)成的方法:
XWPFDocument類中有g(shù)etPictureDataByID(String picture);
方法可以拿到XWPFPictrueDate對象,這個就是圖片的資源了.
具體的操作可以參閱相關(guān)的博文和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<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é)果:
這里使用圖片名稱指代表明我拿到了對應(yīng)的資源,實際上 如果你對前文的內(nèi)容還熟悉的話,會發(fā)現(xiàn)圖片的名稱實際上就是word/media文件夾下的所有圖片的全名稱。
在對應(yīng)的XWPFPictureData對象中,圖像的二進制數(shù)據(jù)可以通過getData()屬性來拿到,這樣你就可以保存到數(shù)據(jù)庫或者是你本地的文件夾中了!
Java主要應(yīng)用于:1. web開發(fā);2. Android開發(fā);3. 客戶端開發(fā);4. 網(wǎng)頁開發(fā);5. 企業(yè)級應(yīng)用開發(fā);6. Java大數(shù)據(jù)開發(fā);7.游戲開發(fā)等。
看完了這篇文章,相信你對“Java解析word怎么獲取文檔中圖片位置”有了一定的了解,如果想了解更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!
網(wǎng)站題目:Java解析word怎么獲取文檔中圖片位置
本文URL:http://muchs.cn/article44/pisohe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動網(wǎng)站建設(shè)、電子商務(wù)、搜索引擎優(yōu)化、、網(wǎng)站建設(shè)、網(wǎng)站維護
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)