Product

解决方案

资源

Company

Product

解决方案

资源

Company

高级电子邮件模板

2016年3月25日

电子邮件

1 min read

高级电子邮件模板

此帖针对希望最大限度发挥 SparkPost 电子邮件模板功能的开发人员。假设您能够舒适地阅读 JSON 内容并遵循基本的编程流程。随着可能您不熟悉的术语的引入,例如 RFC 5322,文本与其来源引用相链接。

这篇文章是针对想要充分利用SparkPost 的电子邮件模板功能的开发人员的。假设您可以轻松阅读JSON内容并遵循基本编程流程。像RFC 5322这样的可能对您来说较新的术语会链接到其参考来源。处理完这些内容后,让我们直接进入正题。

SparkPost 的模板传输功能使发送电子邮件变得简单。这些功能为文本HTML内容提供了一个抽象,这意味着大多数情况下无需直接编码由RFC 5322定义的电子邮件原始格式,之前称为(RFC 822)。但有时您可能需要创建更复杂的消息,这些消息具备其他多用途互联网邮件扩展(MIME)的部分,而这些部分并没有完全通过SparkPost的RESTful接口直接呈现。

简化的电子邮件撰写

首先,让我们回顾一下发送电子邮件的理想场景。使用传输端点来提供文本HTML内容。SparkPost在后台负责撰写有效的RFC 5322电子邮件。SparkPost会把从substitution_data中提取的替换变量插入到文本和HTML内容中。这是为每个收件人在一个通用模板中生成自定义内容的强大方法。

下面是一个包含HTML和文本内容及替换数据的传输示例。

{
  "options": {
    "open_tracking": true,
    "click_tracking": true
  },
  "campaign_id": "christmas_campaign",
  "return_path": "bounces-christmas-campaign@domain.com",
  "metadata": {
    "user_type": "students"
  },
  "substitution_data": {
    "sender": "Big Store Team"
  },
  "recipients": [
    {
      "return_path": "123@bounces.domain.com",
      "address": {
        "email": "wilma@domain.com",
        "name": "Wilma Flintstone"
      },
      "tags": [
        "greeting",
        "prehistoric",
        "fred",
        "flintstone"
      ],
      "metadata": {
        "place": "Bedrock"
      },
      "substitution_data": {
        "customer_type": "Platinum"
      }
    }
  ],
  "content": {
    "from": {
      "name": "Fred Flintstone",
      "email": "fred@domain.com"
    },
    "subject": "Big Christmas savings!",
    "reply_to": "Christmas Sales <sales@domain.com>",
    "headers": {
      "X-Customer-Campaign-ID": "christmas_campaign"
    },
    "text": "Hi {{address.name}} \nSave big this Christmas in your area {{place}}! \nClick http://www.mysite.com and get huge discount\n Hurry, this offer is only to {{user_type}}\n {{sender}}",
    "html": "<p>Hi {{address.name}} <br>Save big this Christmas in your area {{place}}! <br>Click <a href=\"http://www.mysite.com\">here</a> and get huge discount</p><p>Hurry, this offer is only to {{user_type}}</p><p>{{sender}}</p>"
  }
}

替换数据数组

许多人意识到SparkPost的传输和模板端点可以在电子邮件头和正文中进行简单内容替换。但很多人忽略了提供条件内容或数据数组的能力,这些数据同样可以被替换。您还可以为每位收件人提供独特内容。在这个例子中,我们向每位收件人发送一个独一无二的链接数组。

这通过提供一个JSON数据数组来完成,该数组将填充到电子邮件正文中。一旦数据被提供,SparkPost将使用模板中的逻辑将其填充。

在这个例子中,SparkPost会寻找名为“files_html”的替换数据,并对数组中的每个元素进行“for each”操作。它将在“files_html”元素中创建一个包含“file”值的行。注意在“loop_var.file”上的三重花括号。这是因为数组的每个元素都包含HTML,我们需要告诉服务器按原样使用,而不是对其进行转义。文本部分将是一个简单的文本标签和文件的URL。

<table>
  {{each files_html}}
    <tr>
      <td>{{{loop_var.file}}}</td>
    </tr>
  {{ end }}
</table>

这是完整的工作示例:

{
  "recipients": [
    {
      "address": {
        "email": "recipient1@domain.com"
      },
      "substitution_data": {
        "files_html": [
          {
            "file": "<a href=\"http://domain.com/file1a.txt\">File 1a Description</a>"
          },
          {
            "file": "<a href=\"http://domain.com/file2a.txt\">File 2a Description</a>"
          }
        ],
        "files_plain": [
          {
            "file": "File 1a -- http://domain.com/file1a.txt"
          },
          {
            "file": "File 2a -- http://domain.com/file2a.txt"
          }
        ]
      }
    },
    {
      "address": {
        "email": "recipient2@domain.com"
      },
      "substitution_data": {
        "files_html": [
          {
            "file": "<a href=\"http://domain.com/file1b.txt\">File 1b Description</a>"
          },
          {
            "file": "<a href=\"http://domain.com/file2b.txt\">File 2b Description</a>"
          }
        ],
        "files_plain": [
          {
            "file": "File 1b -- http://domain.com/file1b.txt"
          },
          {
            "file": "File 2b -- http://domain.com/file2b.txt"
          }
        ]
      }
    }
  ],
  "return_path": "chris@test.domain.com",
  "content": {
    "from": {
      "name": "chris@test.domain.com",
      "email": "chris@test.domain.com"
    },
    "subject": "Sending with SparkPost is Fun",
    "html": "<b>Your Files:</b><br>\n<table>\n  {{each files_html}}\n    <tr><td>{{{loop_var.file}}}</td></tr>\n  {{ end }}\n</table>\n",
    "text": "Your Files:\n{{each files_plain}} {{loop_var.file}}\n{{ end }}\n"
  }
}

专业提示:在您的代码中,建议将视图标记与数据分开,但这里的目的是让示例尽可能简单和易于理解,因此我们创建了两个数组。一个数组用于HTML部分,另一个用于文本部分。在实际使用中,通常会有一组数据,并在模板代码中编写逻辑。

传输功能中的附件

传输端点还提供了发送附件的抽象。下面您将看到附件被指定在content.attachments数组中,其中数组中的每个对象描述一个单独的附件项。与之前一样,SparkPost将负责编码文本HTML替换并遍历附件数组以编码形成适当格式的电子邮件消息。

最佳实践表明,除非服务明确要求,否则最好避免发送附件。

下面是附件所需的字段:

  • type:附件的MIME类型

  • name:附件的文件名

  • data:Base64编码的文件数据

这就是在传输内容节中附件的外观:

"content": {
  "attachments": [
    {
      "type": "audio/mp3",
      "name": "voicemail.mp3",
      "data": "TVAzIERhdGEK"
    }
  ]
}

您也可以在传输中发送“内联图像”。这些与附件很相似,指定在content.inline_images数组中,其中每个inline_image对象与上面显示的附件对象相似。

模板中的附件

现在我们对在传输端点中发送附件有了适当的背景,让我们来看看如何在模板中做到这一点。在撰写本文时,没有像内联传输一样的附件抽象。有人可能会认为模板不能使用附件。您部分正确,但有一个解决办法,尽管您将不再与RFC 5322格式隔离。

您可以通过自己编码RFC 5322内容来在模板中实现附件,这包括附件。好消息是,您仍然不会失去在电子邮件头HTML文本部分中使用替换数据的能力。请注意,这种类型的模板将替换限制在头部和第一个HTML及第一个文本部分。

这是如何做到的示例。

RFC822电子邮件

创建包含所需替换数据的RFC 5322电子邮件。我在邮件客户端中创建了这个并将其发送给自己。收到之后,我复制了源并替换了想要动态替换的字段。

MIME-Version: 1.0
Reply-To: {{replyto}}
Subject: {{subject}}
From: {{from}}
To: {{address.email}}
Content-Type: multipart/mixed; boundary="001a113c48b0b89d92052d3051da"
--001a113c48b0b89d92052d3051da
Content-Type: multipart/alternative; boundary="001a113c48b0b89d89052d3051d8"
--001a113c48b0b89d89052d3051d8
Content-Type: text/plain; charset=UTF-8
Email with a *text attachment*.
{{body2}}
--001a113c48b0b89d89052d3051d8
Content-Type: text/html; charset=UTF-8
<div dir="ltr">
  <div>Email with a <i>text attachment</i>.</div>
  {{body1}}
<

此消息中的最后一部分MIME您将看到Content-Disposition: attachment; filename=myfile.txt”。那是定义文件名称的地方。您的附件内容很可能会更加复杂,但这个示例尽量保持简单。

存储模板

一旦您有了一个有效的RFC 5322电子邮件,使用email_rfc822形式的模板端点而不是文本HTML字段来存储它。这是该消息内容的示例:

{
  "content": {
    "email_rfc822": "MIME-Version: 1.0\nReply-To: {{replyto}}\nSubject: {{subject}}\nFrom: {{from}}\nTo: {{address.email}}\nContent-Type: multipart/mixed; boundary=001a113c48b0b89d92052d3051da\n\n--001a113c48b0b89d92052d3051da\nContent-Type: multipart/alternative; boundary=001a113c48b0b89d89052d3051d8\n\n--001a113c48b0b89d89052d3051d8\nContent-Type: text/plain; charset=UTF-8\n\nEmail with a *text attachment*.\n\n{{body2}}\n\n--001a113c48b0b89d89052d3051d8\nContent-Type: text/html; charset=UTF-8\n\n<div dir=\"ltr\"><div>Email with a <i>text attachment</i>.</div>\n\n{{body1}}\n</div>\n\n--001a113c48b0b89d89052d3051d8--\n--001a113c48b0b89d92052d3051da\nContent-Type: text/plain; charset=US-ASCII; name=\"myfile.txt\"\nContent-Disposition: attachment; filename=\"myfile.txt\"\nContent-Transfer-Encoding: base64\nX-Attachment-Id: f_ild455ce0\n\nVGhpcyBpcyBteSBzaW1wbGUgdGV4dCBmaWxlLgo=\n--001a113c48b0b89d92052d3051da--"
  },
  "name": "_TMP_TEMPLATE_TEST"
}

一旦请求完成,SparkPost将为您的新模板响应一个唯一标识符。例如xxxxxxx

发送模板

好消息是创建RFC 5322内容是困难的部分。从现在开始,使用SparkPost发送该模板与发送其他模板完全相同。

这是我们如何发送该模板并填充替换数据的方法:

{
  "campaign_id": "MyCampaign",
  "return_path": "myReturnPath@yourdomain.com",
  "substitution_data": {
    "replyto": "myReplyToh@yourdomain.com",
    "from": "MyFrom@yourdomain.com",
    "subject": "my subject",
    "body1": "Extra content for the HTML part",
    "body2": "Extra content for the text part"
  },
  "recipients": [
    {
      "substitution_data": {},
      "address": {
        "email": "test1@domain.com",
        "name": "test1"
      }
    }
  ],
  "content": {
    "template_id": "xxxxxxx",
    "use_draft_template": true
  }
}

邮件客户端API中的模板

如果您使用的编程语言有一个用于撰写电子邮件的库,您可以使用该库程序化地创建模板或甚至内联发送消息。这是通过SparkPost的传输端点使用JavaMail的示例。此方法应该可以轻松转换为PHP或您选择的语言。

/**
 * Demonstration of using JavaMail MIME message with the SparkPost RESTful interface
 */
public class App extends SparkPostBaseApp {
    public static void main(String[] args) throws Exception {
        Logger.getRootLogger().setLevel(Level.DEBUG);
        App app = new App();
        app.runApp();
    }
    private void runApp() throws Exception {
        Message message = createMultipartMessage();
        // Convert JavaMail message into a string for transmission
        String rfc822Content = getMessageAsString(message);
        // Add dynamic To and From using SparkPost substitution syntax
        rfc822Content = "To: {{address.email}}\r\nFrom: {{from}}\r\n" + rfc822Content;
        String fromAddress = getFromAddress();
        String[] recipients = getTestRecipients();
        sendEmail(fromAddress, recipients, rfc822Content);
    }
    private void sendEmail(String from, String[] recipients, String email)
            throws SparkPostException, IOException {
        Client sparkpostClient = newConfiguredClient();
        TransmissionWithRecipientArray transmission = new TransmissionWithRecipientArray();
        // Populate Recipients
        List<RecipientAttributes> recipientArray = new ArrayList<>();
        for (String recipient : recipients) {
            RecipientAttributes recipientAttribs = new RecipientAttributes();
            recipientAttribs.setAddress(new AddressAttributes(recipient));
            recipientArray.add(recipientAttribs);
        }
        transmission.setRecipientArray(recipientArray);
        transmission.setReturnPath(from);
        // Populate Substitution Data
        Map<String, String> substitutionData = new HashMap<>();
        substitutionData.put("from", from);
        substitutionData.put("name", "Your Name Here");
        transmission.setSubstitutionData(substitutionData);
        // Populate Email Body with RFC822 MIME
        TemplateContentAttributes contentAttributes = new TemplateContentAttributes();
        contentAttributes.setEmailRFC822(email);
        transmission.setContentAttributes(contentAttributes);
        // Send Email
        RestConnection connection = new RestConnection(sparkpostClient, getEndPoint());
        Response response = ResourceTransmissions.create(connection, 0, transmission);
        if (response.getResponseCode() == 200) {
            System.out.println("✅ Transmission Response: " + response);
        } else {
            System.err.println("❌ TRANSMISSION ERROR: " + response);
        }
    }
    /**
     * Builds an email with text, HTML, and attachment parts
     */
    private Message createMultipartMessage() throws MessagingException {
        Properties props = new Properties();
        props.put("mail.smtp.host", "none"); // Required for JavaMail to work
        Session session = Session.getDefaultInstance(props, null);
        Message message = new MimeMessage(session);
        message.setSubject("A multipart MIME message demo");
        // Main multipart container
        Multipart multiPart = new MimeMultipart("mixed");
        // Sub multipart for text + HTML
        MimeMultipart altPart = new MimeMultipart("alternative");
        // Text part
        MimeBodyPart textPart = new MimeBodyPart();
        textPart.setText("{{name}},\r\nplain text content", "utf-8");
        // HTML part
        MimeBodyPart htmlPart = new MimeBodyPart();
        htmlPart.setContent("<b>{{name}},<br><br>Our HTML content</b>", "text/html; charset=utf-8");
        // Add text and HTML to the alternative container
        altPart.addBodyPart(textPart);
        altPart.addBodyPart(htmlPart);
        // Wrap alternative part in a MimeBodyPart so it can be added to mixed container
        MimeBodyPart altBodyPart = new MimeBodyPart();
        altBodyPart.setContent(altPart);
        // Add alternative section to mixed container
        multiPart.addBodyPart(altBodyPart);
        // Add attachment
        MimeBodyPart attachmentPart = new MimeBodyPart();
        String filename = "java_SparkPost_background.pdf";
        DataSource source = new FileDataSource(filename);
        attachmentPart.setDataHandler(new DataHandler(source));
        attachmentPart.setFileName(filename);
        multiPart.addBodyPart(attachmentPart);
        // Set full content
        message.setContent(multiPart);
        return message;
    }
    /**
     * Converts a JavaMail message into an RFC822 string
     */
    private String getMessageAsString(Message msg) throws IOException, MessagingException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            msg.writeTo(out);
            return out.toString("UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("UTF-8 not found! " + e.getMessage());
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

结论

现在您看到了如何使用SparkPost发送几乎任何复杂程度的电子邮件,您可能想要查看“SparkPost支持在Apple Watch上发送电子邮件”或查看替换语法以了解如何在模板或传输内容中使用“if then else”、条件表达式或数组迭代表达式。

其他新闻

阅读更多来自此类别的内容

A person is standing at a desk while typing on a laptop.

这个完整的AI原生平台可以随着您的业务进行扩展。

© 2025 Bird

A person is standing at a desk while typing on a laptop.

这个完整的AI原生平台可以随着您的业务进行扩展。

© 2025 Bird