天天看點

Java操作Office:POI之word生成

作者:流火星空26131197

程式員架構進階

一 背景

最近在項目開發中,有資料導出到word的需求。這就涉及代碼生成word文檔的操作,且有格式要求。大家用word做過履歷的都有了解,做履歷時,會使用表格、圖檔、文字等元素。而且表格也可能有嵌套、合并單元格,以及插入圖檔到單元格的操作。該怎麼做?

二 Java操作Office方案

百度一下Java Office操作,或者再直接一點搜尋Java word,就比較容易搜到iText、POI等元件。在文章 Java導出word的幾種方式 這篇文章中,提到了包括Jacob、Apache POI、Java2word、iText、FreeMarker五種方式。

通過對比,結合需求要求,最終選擇了Apache POI來實作,是以這裡先詳細介紹POI,以及一個可用的demo,供參考。

三 ApachePOI

Apache POI(官網)是基于Office Open XML标準(OOXML)和Microsoft的OLE 2複合文檔格式(OLE2)處理各種檔案格式的開源項目。簡而言之,您可以使用Java讀寫MS Excel檔案,可以使用Java讀寫MS Word和MS PowerPoint檔案。

poi的gitee位址:gitee。入門教程可以參考 Apache POI Word(docx) 入門示例教程。

四 版本資訊

poi的最新版本已經到了5.0.0,不過可以找到的大部分demo都是基于3.x版本或4.1版本。為了盡快搭建demo并運作起來,我們也沒有使用最新版本,而是選擇了4.1.0進行開發。

4.1 引用依賴

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <poi.version>4.1.0</poi.version>
</properties>




<dependencies>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>${poi.version}</version>
    </dependency>


    <!-- poi處理xlsx格式,用于處理word中的表格 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>${poi.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-excelant</artifactId>
        <version>${poi.version}</version>
    </dependency>


    <!-- poi-tl基于poi的word模闆引擎 -->
    <dependency>
        <groupId>com.deepoove</groupId>
        <artifactId>poi-tl</artifactId>
        <version>1.5.0</version>
    </dependency>


    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
    </dependency>
</dependencies>
           

4.2 建立word示例代碼

4.2.1 建立新的文檔

建立word文檔比較簡單,直接使用new XWPFDocument即可,XWPFDocument是對 .docx 文檔操作的進階封裝API:

XWPFDocument doc = new XWPFDocument();           

4.2.2 表格

即Word文檔中的表格。API建立時需要指定行數和列數,示例如下:

//建立一個表格,并指定寬度
XWPFTable table = doc.createTable(4, 4);
TableTools.widthTable(table, MiniTableRenderData.WIDTH_A4_FULL, 4);


//設定第0行資料
List<XWPFTableCell> row0 = table.getRow(0).getTableCells();
row0.get(0).setText("xxxx"); //為第0行第0列設定内容
row0.get(0).setWidth("200");
row0.get(1).setText("aaaa");
row0.get(2).setText("bbbb");
row0.get(3).setText("cccc");
           

正常的簡單表格,我們隻要按照上述代碼逐行操作即可;但現實中不會這麼容易。通常會涉及在單元格插入圖檔、合并行、合并列,甚至表格嵌套。目前表格嵌套暫未實作,先介紹其他三種情況。

4.2.3 列合并

有兩種方法,一種是使用addNewHMerge方法,通過設定合并的起始列和結束列,逐個列進行合并:

List<XWPFTableCell> row2_1 = table.getRow(2).getTableCells();
row2_1.get(0).setText("合并表格"); //為第0行第0列設定内容
//将第一列到第四列合并
for (int i = 1; i <= 3; i++) {
    //對單元格進行合并的時候,要标志單元格是否為起點,或者是否為繼續合并
    if (i == 1)
        row2_1.get(i).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);//這是起點
    else
        row2_1.get(i).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);//繼續合并
}
           

在業務代碼中這樣的寫法稍顯繁瑣,我們也可以直接使用TableTools.mergeCellsHorizonal()函數來執行合并:

// 合并第一行的第0列到第8列單元格
TableTools.mergeCellsHorizonal(table, 1, 0, 8);           

4.2.4 行合并

如果是要合并某幾行,也可以使用TableTools提供的方法:

// 合并第0列的第一行到第九行的單元格
TableTools.mergeCellsVertically(table, 0, 1, 9);           

我們看一下TableTools的源碼:

public static void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
        if (toRow > fromRow) {
            for(int rowIndex = fromRow; rowIndex <= toRow; ++rowIndex) {
                XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
                CTTcPr tcPr = getTcPr(cell);
                CTVMerge vMerge = tcPr.addNewVMerge();
                if (rowIndex == fromRow) {
                    vMerge.setVal(STMerge.RESTART);
                } else {
                    vMerge.setVal(STMerge.CONTINUE);
                }
            }


        }
    }
           

可以發現,底層還是使用addNewVMerge等方法,也設定了起始和結束位置,隻是做了一層封裝。

4.2.5 圖檔插入表格

圖檔插入表格要麻煩一些,如果大家在百度上搜尋過插入圖檔到表格方法,大機率會找到這樣的操作:

Java操作Office:POI之word生成

大部分對應的都是3.9以前的版本,寫起來比較複雜,而且在4.x之後,圖中super.getRelationId()方法也發生了變化,代碼報錯。

通過調研,發現XWPFRun中提供了addPicture方法,寫起來也簡單了很多。一個示例如下:

String imageFile = "/Users/xxx/Downloads/圖檔 1.png";
InputStream stream = new FileInputStream(imageFile);


//表格中建立段落
XWPFParagraph paragraph = row2_1.get(1).getParagraphs().get(0);
XWPFRun run = paragraph.createRun();
run.addPicture(stream, XWPFDocument.PICTURE_TYPE_PNG, "Generated",
    Units.toEMU(364), Units.toEMU(256));
           

run.addPicture接收的參數依次為:圖檔的InputStream流,圖檔類型,圖檔名稱(非檔案名),圖檔寬度、圖檔高度。通過這個方法,我們就可以把圖檔插入到指定的表格中,并設定圖檔的寬高屬性。

五 總結

通過上述介紹,大家應該可以簡單實作一個表格了。本文的方式還是偏向于寫死的方式,在很多場景(例如履歷、報表等典型場景)可以采用模闆的方式,建立word模闆,然後用模闆内容替換來生成複雜樣式的表格。這個在後續文章中再做介紹,大家也可以先搜尋相關的實作來學習了解。