這篇文章介紹瞭如何使用 Java 在 MS Word 文檔中執行郵件合併操作。到本文結束時,您將了解如何創建郵件合併模板並以編程方式執行郵件合併。

關於郵件合併

郵件合併 是動態生成信件、信封、發票、報告和其他類型文檔的便捷方式。使用郵件合併,您可以創建一個包含合併字段的模板文件,然後使用數據源中的數據填充這些字段。假設您要將一封信寄給 20 個不同的人,您只需更改每封信件上收件人的姓名和地址。在這種情況下,您可以為這封信創建一個郵件合併模板,然後通過動態填充姓名和地址字段來生成 20 封信。

Java 郵件合併 API - 免費下載

Aspose.Words for Java 是一個著名的文字處理 API,可讓您從頭開始創建各種類型的文檔。 API 提供內置的郵件合併功能,允許您使用模板和數據源動態生成文檔。 Aspose.Words for Java 可以下載為 JAR 或安裝在基於 Maven 的應用程序中。

<repository>
    <id>AsposeJavaAPI</id>
    <name>Aspose Java API</name>
    <url>https://repository.aspose.com/repo/</url>
</repository>
<dependency>
    <groupId>com.aspose</groupId>
    <artifactId>aspose-words</artifactId>
    <version>20.11</version>
    <classifier>jdk17</classifier>
</dependency>

郵件合併的數據源

郵件合併中的數據可以從任何數據源獲取,例如 JSON、XML、電子表格或數據庫。

在 MS Word 中創建郵件合併模板

郵件合併中使用的模板可以是一個簡單的 Word 文檔(即 DOCX)並且不需要是模板格式。模板文檔包含在執行郵件合併時用數據填充的合併字段。以下是如何使用 MS Word 準備郵件合併模板的步驟。

  • 在 MS Word 中創建一個新文檔。
  • 將光標置於要添加合併字段的位置。
  • 從插入菜單中選擇字段選項。
  • 從字段名稱列表中,選擇 MergeField。
  • 在字段名稱中輸入合併字段的名稱,然後按確定。
  • 將文檔另存為 DOCX。

以下是示例模板文檔的截圖。

郵件合併word模板

使用 Java 創建郵件合併模板

您還可以通過編程方式生成郵件合併模板。以下是它的步驟。

以下代碼示例顯示瞭如何使用 Java 創建郵件合併模板。

// 創建文檔生成器
DocumentBuilder builder = new DocumentBuilder();

// 插入一個文本輸入字段,這個字段的唯一名稱是“Hello”,其他參數定義
// 它是什麼類型的 FormField、文本格式、字段結果和最大文本長度(0 = 無限制)
builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "", "Hello", 0);
builder.insertField("MERGEFIELD CustomerFirstName \\* MERGEFORMAT");

builder.insertTextInput("TextInput1", TextFormFieldType.REGULAR, "", " ", 0);
builder.insertField("MERGEFIELD CustomerLastName \\* MERGEFORMAT");

builder.insertTextInput("TextInput1", TextFormFieldType.REGULAR, "", " , ", 0);

// 在文檔中插入段落分隔符
builder.insertParagraph();

// 插入郵件正文
builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "", "Thanks for purchasing our ", 0);
builder.insertField("MERGEFIELD ProductName \\* MERGEFORMAT");

builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "", ", please download your Invoice at ",
	0);
builder.insertField("MERGEFIELD InvoiceURL \\* MERGEFORMAT");

builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "",
	". If you have any questions please call ", 0);
builder.insertField("MERGEFIELD Supportphone \\* MERGEFORMAT");

builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "", ", or email us at ", 0);
builder.insertField("MERGEFIELD SupportEmail \\* MERGEFORMAT");

builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "", ".", 0);

builder.insertParagraph();

// 插入郵件結尾
builder.insertTextInput("TextInput", TextFormFieldType.REGULAR, "", "Best regards,", 0);
builder.insertBreak(BreakType.LINE_BREAK);
builder.insertField("MERGEFIELD EmployeeFullname \\* MERGEFORMAT");

builder.insertTextInput("TextInput1", TextFormFieldType.REGULAR, "", " ", 0);
builder.insertField("MERGEFIELD EmployeeDepartment \\* MERGEFORMAT");

// 保存文件
builder.getDocument().save("document.docx");

使用 Java 在 Word 文檔中執行郵件合併

模板準備就緒後,您可以使用數據填充合併字段。以下是在 Word 模板上執行郵件合併的步驟。

以下代碼示例顯示如何使用 Java 在 Word 文檔中執行郵件合併。

// 包括我們模板的代碼
Document doc = new Document();

// 將文檔傳遞給文檔生成器
DocumentBuilder builder = new DocumentBuilder(doc);

// 創建合併字段
builder.insertField(" MERGEFIELD CustomerName ");
builder.insertParagraph();
builder.insertField(" MERGEFIELD Item ");
builder.insertParagraph();
builder.insertField(" MERGEFIELD Quantity ");

// 保存模板
builder.getDocument().save("MailMerge.TestTemplate.docx");

// 用用戶數據填充文檔中的字段
doc.getMailMerge().execute(new String[] { "CustomerName", "Item", "Quantity" },
		new Object[] { "John Doe", "Hawaiian", "2" });

// 保存文檔 
builder.getDocument().save("MailMerge.Simple.docx");

模板

java中的簡單郵件合併

輸出

簡單郵件合併

使用 XML 數據源執行郵件合併

在前面的示例中,我們使用 Java 對象執行郵件合併。但是,在大多數情況下,數據源用於填充合併字段。為了演示,讓我們看看如何在郵件合併中使用 XML 數據源。以下是它的步驟。

  • 使用 DataSet 類加載 XML 數據源。
  • 使用 Document 類加載郵件合併模板。
  • 使用執行函數使用數據源中所需的數據表填充合併字段。
  • 使用 Document.save(String) 方法保存生成的 Word 文檔。

以下是此示例中使用的 XML 數據源。

<?xml version="1.0" encoding="utf-8"?>
<customers>
	 <customer Name="John Ben Jan" ID="1" Domain="History" City="Boston"/>
 	<customer Name="Lisa Lane" ID="2" Domain="Chemistry" City="LA"/>
	 <customer Name="Dagomir Zits" ID="3" Domain="Heraldry" City="Milwaukee"/>
 	<customer Name="Sara Careira Santy" ID="4" Domain="IT" City="Miami"/>
</customers> 

以下代碼示例顯示如何使用提供的 XML 數據源中的客戶數據表填充郵件合併模板。

// 創建數據集並讀取 XML
DataSet customersDs = new DataSet();
customersDs.readXml("Customers.xml");

// 打開模板文檔
Document doc = new Document("TestFile XML.docx");

// 執行郵件合併以使用 DataTable 使用來自 XML 的數據填充模板。
// 請注意,此類也適用於單個可重複區域(以及任何嵌套區域)。
// 要同時從單個 XML 數據源合併多個區域,請使用 XmlMailMergeDataSet 類。
// 例如 doc.getMailMerge().executeWithRegions(new XmlMailMergeDataSet(xmlData));
doc.getMailMerge().execute(customersDs.getTables().get("Customer"));

// 保存輸出文件
doc.save("generated-document.docx");

模板

郵件與 XML 合併

輸出

郵件合併 xml

郵件與 Java 中的區域合併

在某些情況下,您可能需要重複文檔中的特定區域。例如,您想要在單獨的表中顯示每個客戶所下的訂單。在這種情況下,您可以利用郵件合併區域。為了創建區域,您可以指定區域的起點和終點。因此,在郵件合併執行期間,將為每個數據實例重複該區域。

以下屏幕截圖顯示了一個模板,其中區域由一個表組成。它以 «TableStart:Customers» 開始,以 «TableEnd:Customers» 結束。

郵件合併區域模板

以下代碼示例顯示瞭如何創建包含區域的模板並使用數據填充它。

// 創建文檔
Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);

// 郵件的起點與數據集的區域合併。
builder.insertField(" MERGEFIELD TableStart:Customers");
// 來自“Customers”表的“CustomerName”列的行的數據將去
// 在這個 MERGEFIELD 中。
builder.write("Orders for ");
builder.insertField(" MERGEFIELD CustomerName");
builder.write(":");

// 創建列標題
builder.startTable();
builder.insertCell();
builder.write("Item");
builder.insertCell();
builder.write("Quantity");
builder.endRow();

// 我們有第二個數據表叫做“Orders”,它有一個多對一的
// 與“客戶”的關係
// 選取具有相同 CustomerID 值的行。
builder.insertCell();
builder.insertField(" MERGEFIELD TableStart:Orders");
builder.insertField(" MERGEFIELD ItemName");
builder.insertCell();
builder.insertField(" MERGEFIELD Quantity");
builder.insertField(" MERGEFIELD TableEnd:Orders");
builder.endTable();

// 郵件與區域合併的終點。
builder.insertField(" MERGEFIELD TableEnd:Customers");

// 傳遞我們的數據集以執行與區域的郵件合併。
DataSet customersAndOrders = CreateDataSet();
doc.getMailMerge().executeWithRegions(customersAndOrders);

// 保存結果
doc.save("MailMerge.ExecuteWithRegions.docx");

輸出

郵件合併區域

使用 Java 創建嵌套郵件合併區域

郵件合併中的另一個流行場景是當您有嵌套區域時。例如,當您必須列出訂單和每個訂單中的項目時,您可以使用嵌套區域。下圖使嵌套區域的圖片更加清晰。

在上圖中,我們有 Orders 表和 Items 表,其中 Items 中的每條記錄都鏈接到 Orders 中的一條記錄。因此,這兩個表之間存在一對多關係。在這種情況下,Aspose.Words 執行郵件合併處理定義在數據集中的關係。例如,如果我們有一個 XML 數據源,那麼 Aspose.Words 將使用模式信息或 XML 的結構來找出關係。因此,您不必自己手動處理它,Document.getMailMerge().executeWithRegions(DataSet) 方法將為您工作(如前例)。

在合併字段上應用自定義格式

為了讓您更好地控制郵件合併,Aspose.Words for Java 允許您在郵件合併執行期間自定義合併字段。 setFieldMergingCallback(IFieldMergingCallback) 方法接受一個實現 fieldMerging(FieldMergingArgs)imageFieldMerging(ImageFieldMergingArgs) 方法的類,用於自定義控制郵件合併過程。 fieldMerging(FieldMergingArgs) 事件在郵件合併執行期間遇到合併字段時發生。

以下是有關如何自定義郵件合併操作並將格式應用於單元格的完整代碼示例。

public class ApplyCustomFormattingDuringMailMerge {

    private static final String dataDir = Utils.getSharedDataDir(ApplyCustomFormattingDuringMailMerge.class) + "MailMerge/";

    public static void main(String[] args) throws Exception {
        Document doc = new Document(dataDir + "MailMerge.AlternatingRows.doc");

        // 為 MergeField 事件添加處理程序。
        doc.getMailMerge().setFieldMergingCallback(new HandleMergeFieldAlternatingRows());

        // 執行與區域的郵件合併。
        DataTable dataTable = getSuppliersDataTable();
        doc.getMailMerge().executeWithRegions(dataTable);

        doc.save(dataDir + "MailMerge.AlternatingRows Out.doc");
    }

    /**
     * 如果值為奇數,則返回真;如果值為偶數,則為 false。
     */
    public static boolean isOdd(int value) throws Exception {
        return (value % 2 != 0);
    }

    /**
     * 創建 DataTable 並用數據填充它。在現實生活中這個 DataTable
     * 應該從數據庫中填充。
     */
    private static DataTable getSuppliersDataTable() throws Exception {
        java.sql.ResultSet resultSet = createCachedRowSet(new String[]{"CompanyName", "ContactName"});

       for (int i = 0; i < 10; i++)
            addRow(resultSet, new String[]{"Company " + Integer.toString(i), "Contact " + Integer.toString(i)});

        return new DataTable(resultSet, "Suppliers");
    }

    /**
     * 創建一個空的 Java 斷開連接的 ResultSet 的輔助方法
     * 指定的列。
     */
    private static ResultSet createCachedRowSet(String[] columnNames) throws Exception {
        RowSetMetaDataImpl metaData = new RowSetMetaDataImpl();
        metaData.setColumnCount(columnNames.length);
       for (int i = 0; i < columnNames.length; i++) {
            metaData.setColumnName(i + 1, columnNames[i]);
            metaData.setColumnType(i + 1, java.sql.Types.VARCHAR);
        }

        CachedRowSet rowSet = RowSetProvider.newFactory().createCachedRowSet();
        ;
        rowSet.setMetaData(metaData);

        return rowSet;
    }

    /**
     * 將具有指定值的新行添加到
     * 斷開連接的結果集。
     */
    private static void addRow(ResultSet resultSet, String[] values) throws Exception {
        resultSet.moveToInsertRow();

       for (int i = 0; i < values.length; i++)
            resultSet.updateString(i + 1, values[i]);

        resultSet.insertRow();

        // 需要這種“舞蹈”才能正確地將行添加到結果集的末尾。
        // 如果我做其他事情,那麼行要么被添加在前面,要么被添加到結果中
        // set 在郵件合併期間拋出有關已刪除行的異常。
        resultSet.moveToCurrentRow();
        resultSet.last();
    }
}

class HandleMergeFieldAlternatingRows implements IFieldMergingCallback {
    /**
     * 為文檔中遇到的每個合併字段調用。我們可以
     * 將一些數據返回到郵件合併引擎或用
     * 文檔。在這種情況下,我們修改單元格格式。
     */
    public void fieldMerging(FieldMergingArgs e) throws Exception {
        if (mBuilder == null)
            mBuilder = new DocumentBuilder(e.getDocument());

        // 這樣我們就抓住了新行的開頭。
        if (e.getFieldName().equals("CompanyName")) {
            // 根據行號是偶數還是奇數選擇顏色。
            Color rowColor;
            if (ApplyCustomFormattingDuringMailMerge.isOdd(mRowIdx))
                rowColor = new Color(213, 227, 235);
            else
                rowColor = new Color(242, 242, 242);

            // 目前無法為整行設置單元格屬性,
            // 所以我們必須遍歷行中的所有單元格。
           for (int colIdx = 0; colIdx < 4; colIdx++) {
                mBuilder.moveToCell(0, mRowIdx, colIdx, 0);
                mBuilder.getCellFormat().getShading().setBackgroundPatternColor(rowColor);
            }

            mRowIdx++;
        }
    }

    public void imageFieldMerging(ImageFieldMergingArgs args) throws Exception {
        // 沒做什麼。
    }

    private DocumentBuilder mBuilder;
    private int mRowIdx;
}

結論

郵件合併為您提供了廣泛的功能來動態生成 MS Word 文檔。您可以根據數據庫或其他數據源中的數據生成簡單和復雜的報告。在本文中,您了解瞭如何使用 Java 以編程方式實現郵件合併功能。您可以使用 文檔 了解有關 Java 郵件合併 API 的更多信息。

也可以看看