这篇文章介绍了如何使用 Java 在 MS Word 文档中执行邮件合并操作。在本文结束时,您将了解如何创建邮件合并模板并以编程方式执行邮件合并。

关于邮件合并

Mail Merge 是一种动态生成信件、信封、发票、报告和其他类型文档的便捷方式。使用邮件合并,您可以创建一个包含合并字段的模板文件,然后使用数据源中的数据填充这些字段。假设您要向 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 中创建邮件合并模板

Mail Merge 中使用的模板可以是简单的 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 对象执行了邮件合并。但是,在大多数情况下,数据源用于填充合并字段。为了演示,让我们看看如何在 Mail Merge 中使用 XML 数据源。以下是它的步骤。

  • 使用 DataSet 类加载 XML 数据源。
  • 使用 Document 类加载邮件合并模板。
  • 使用 execute 函数在数据源中使用所需的数据表填充合并字段。
  • 使用 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");
// “客户”表的“客户名称”列中的数据将转到
// 在这个 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");
    }

    /**
     * 如果值为奇数,则返回 true;如果值是偶数,则为 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;
}

结论

Mail Merge 为您提供了广泛的功能来动态生成 MS Word 文档。您可以根据数据库或其他数据源中的数据生成简单和复杂的报告。在本文中,您了解了如何使用 Java 以编程方式实现邮件合并功能。您可以使用 documentation 了解有关 Java Mail Merge API 的更多信息。

也可以看看