前置瞭解
這裡僅說明以docx字尾的word檔案,doc檔案似乎用的是另一個api,不是很瞭解。
首先在使用poi之前,需要了解word.docx的儲存方式。右鍵word文件,我們可以發現選項中是有開啟壓縮包等選項的。解壓縮後資料夾格式如下:
-word
-_rels
- .rels
-customXml
...
-docProps
...
-media
- 1.png
- 2.png
- ...
-word
...
- document.xml
...
[Content_Types].xml
複製程式碼
其中我們需要關注的主要是media資料夾,它下面是該word文件中所有的圖片;以及word資料夾下的document.xml,該xml檔案統領全域性,包括了word文件中的文字、格式、圖片的引用等等。
使用poi
如果使用maven的話,可以新增如下引用:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>3.17</version>
</dependency>
複製程式碼
如果需要處理word中的數學公式,那麼還需要新增:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
<version>1.0</version>
</dependency>
複製程式碼
document
首先是獲取文件的內容,XWPFDocument document = new XWPFDocument(new FileInputStream(filePath));
接下來說下document的整體格式。
此時我們獲取的document,很大程度上就是對應了之前word資料夾下的document.xml檔案,我們之後也會很多的使用xml的方式來處理word文件。用文字編輯器開啟document.xml,這個檔案是經過混淆的,我這裡使用sublime的外掛來進行重新排版。
從document.xml檔案可以看到,根節點w:document下緊跟的是w:body,body節點下幾乎包括了所有的檔案資訊。body下是三種節點:
1.<w:sectPr>:該節點下定義了該word文件的整體資訊,包括頁首頁尾的引用,你可以在解壓後的word資料夾下找到頁首頁尾的定義。另外也包括了頁面的大小。這一部分我在使用過程中不需要獲取,具體內容也不是很瞭解。可以在複製新的document時使用document方法直接整體set進去(如果不需要獲取格式內容的話,接下來的段落、表格及它們內部的節點同理,格式部分直接用原來的設定);
2.<w:p>:段落,對應XWPFParagraph。段落下主要也有三個節點
1.<w:pPr>與sectPr同理,格式部分內容,接下來的各部分的格式部分就不在重複了,基本上每一個節點都擁有一個屬性,對應poi中的一個類
2.<w:run>run標籤,對應XWPFRun,是段落中格式相同的一部分內容,主要包括了屬性部分,圖片部分,文字部分。也就是說,一個段落中如果有多個格式的文字,或是圖片,那麼該段落節點下相應的就會有多個run標籤。
--------1.文字部分:在text標籤下,設定和獲取都很簡單,相應物件中有直接的方法
--------2.圖片部分:<w:drawing>標籤,對應XWPFPictureData。需要注意的是,word中每一張圖片都對應一個Id:
<pic:blipFill>
<a:blip cstate="print" r:embed="rId15"/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</pic:blipFill>
複製程式碼
找到其中的該部分內容,其中我們可以得到該圖片的id為rId15,我們可以直接通過XWPFPictureData picture = document.getPictureDataByID(id);
來獲取指定的圖片,獲取id的方法放到文章末尾。這個picture擁有getBytes方法,之後想怎麼做就看你了=。=另外網上有同學說可能有另一種格式來儲存圖片,不過我在使用過程中未遇到該種情況,所以不細述。
3.<m:oMath>公式部分,需要新增文章開頭的依賴,對應CTOMath。如果不需要獲取公式的話可以跳過。如果大家需要用到公式的話,很可能就需要將他用pandoc將其轉成latex格式。我在使用過程中遇到的問題是部門老大的mac無法讀取word中的公式部分,以及在使用pandoc轉檔案格式時會忽略其中的公式,而在windows下是能夠正常通過word檢視的。最後發現是該部分標籤放在的run標籤下,這導致了這兩個問題。所以我當時需要做的很簡單,遍歷run標籤內元素,遇到公式,將run標籤內的公式部分刪去,獲取父段落,新增到與run標籤同級。遍歷部分與獲取圖片的id方法一樣,在最後說。
3.<w:tab>表格,對應XWPFTable。表格內除了格式屬性外,就是每一行,下面是列,單元格,我們在閱讀word文件時可以發現,每一個單元格的內容後都有一個回車符號,這是因為單元格下就是使用<w:p>段落來儲存的,所以知道了上面知識,表格也就簡單了。
遍歷
接下來說下遍歷的方法。如果僅僅需要段落、圖片或是表格的一種,那麼XWPFDocument下有直接的方法來獲取;如果需求相對複雜的話,使用XWPFDocument:getBodyElements獲取所有元素。通過instanceof判斷格式。對於具體的,可以通過:
target.getCTR().setRPr(source.getCTR().getRPr());
XmlCursor cursor = source.getCTR().newCursor();
cursor.selectPath("./*");
while (cursor.toNextSelection()) {
XmlObject xmlObject = cursor.getObject();
if (xmlObject instanceof CTText) {
CTText ctText = (CTText) xmlObject;
target.setText(ctText.getStringValue());
} else if (xmlObject instanceof XmlAnyTypeImpl) {
CTOMath ctoMath = paragraph.getCTP().addNewOMath();
ctoMath.set(xmlObject);
}
}
複製程式碼
來進行,上述是我複製run標籤並刪除run內部的段落的部分程式碼,可以簡單參考下。其中公式在run內部時的class型別是XmlAnyTypeImpl。cursor.selectPath("./*");
的意思是獲取所有子元素。
End
那麼文件部分就簡單講到這了,poi中具體的方法還是需要大家自己探索了。祝大家新年快樂!