亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java解析word怎么獲取文檔中圖片位置

發布時間:2021-05-22 11:41:23 來源:億速云 閱讀:390 作者:小新 欄目:編程語言

小編給大家分享一下Java解析word怎么獲取文檔中圖片位置,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

前言(背景介紹):

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整體結構定義:

<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:文檔段落內容:

<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:圖片內容定義:

<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代碼,我這里直接給結論了:

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

文檔根節點:<w:document> 定義了整個文檔的開始

<w:body>是document的子節點,文檔的主體內容

<w:p>body子節點,一個段落,就是word文檔中的段落

<w:r>P元素的子節點,一個Run定義了段落中具有相同格式的一段內容

<w:t>Run元素節點的子節點,就是文檔的內容.

<w:drawing> run元素的子節點,定義了一張圖片:

<w:inline> drawing子節點,具體應用也沒有深入研究

<a:graphic> 定義圖片內容

<pic:blipfill>這個是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<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解析,需要先轉化成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> 對應XWPFDocument類

<w:run>對應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();
//如果子元素是<w:drawing>這樣的形式,使用CTDrawing保存圖片
if (o instanceof CTDrawing) {
CTDrawing drawing = (CTDrawing) o;

最后你可能會有疑問,不是說<w:drawing>這個元素定義了一張圖片嗎?

那么

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

這個第二個判斷條件是用來干嘛的?

聰明的你應該已經猜到了

沒錯!docx文檔中的xml定義圖片的方式除了<w:drawing>這一種之外,還可以運用<w:object>元素去定義,

為什么只有這兩種?

因為我只使用第一種方式解析,發現有些圖片丟失了,于是發現了第二種方式.......也許不止兩種?我也不知道,反正對于目前的我來說已經沒有問題了.

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

那么運用上面提到的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<XWPFParagraph> paragraphList = xwpfDocument.getParagraphs();
 System.out.println("圖片的索引\t|圖片名稱\t|圖片上一段文字的內容\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);
 }
 }
 }
 }
}

展示結果:

Java解析word怎么獲取文檔中圖片位置

這里使用圖片名稱指代表明我拿到了對應的資源,實際上 如果你對前文的內容還熟悉的話,會發現圖片的名稱實際上就是word/media文件夾下的所有圖片的全名稱。

在對應的XWPFPictureData對象中,圖像的二進制數據可以通過getData()屬性來拿到,這樣你就可以保存到數據庫或者是你本地的文件夾中了!

Java可以用來干什么

Java主要應用于:1. web開發;2. Android開發;3. 客戶端開發;4. 網頁開發;5. 企業級應用開發;6. Java大數據開發;7.游戲開發等。

看完了這篇文章,相信你對“Java解析word怎么獲取文檔中圖片位置”有了一定的了解,如果想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

大关县| 扶绥县| 绥滨县| 吕梁市| 阿拉善右旗| 永嘉县| 兴化市| 抚宁县| 鄂温| 那曲县| 礼泉县| 揭阳市| 龙南县| 姜堰市| 东乡族自治县| 中阳县| 望城县| 花莲市| 延津县| 高雄县| 庐江县| 水城县| 汾阳市| 巩义市| 泌阳县| 宝鸡市| 托里县| 维西| 麻阳| 黄大仙区| 公安县| 绥化市| 修水县| 中方县| 盐亭县| 怀化市| 金门县| 葫芦岛市| 阿巴嘎旗| 呼图壁县| 双流县|