Reach

Grow

Manage

Automate

Reach

Grow

Manage

Automate

高级电子邮件模板

2016年3月25日

电子邮件

1 min read

高级电子邮件模板

2016年3月25日

电子邮件

1 min read

高级电子邮件模板

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

Business in a box.

探索我们的解决方案。

这篇文章是面向希望充分利用SparkPost 的电子邮件模板功能的开发者。假设您已熟悉阅读 JSON 内容并遵循基本的编程流程。引入了可能对您来说是新的术语,例如RFC 5322,文本链接到其源参考。说完这些,让我们直接开始吧。

SparkPost 的模板传输功能简化了发送电子邮件的过程。这些功能为文本HTML内容提供了抽象层,这意味着大多数情况下无需直接编码由RFC 5322定义的原始电子邮件格式,该格式以前称为RFC 822。但有时您可能希望创建更复杂的消息,这些消息包含通过 SparkPost 的 RESTful 接口未直接暴露的多用途互联网邮件扩展(MIME)部分。

简化的电子邮件撰写

首先,让我们回顾一下发送电子邮件的理想情况。使用传输端点提供textHTML内容。在幕后,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形式而不是textHTML字段来存储它。以下是该消息的content的示例:

{
  "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 的模板

如果您使用的是编程语言,并且有一个用于撰写电子邮件的库,则可以使用它来编程地创建模板或甚至内联发送消息。这里是一个使用 JavaMail 通过 SparkPost 的传输端点执行这些操作的示例。这种方法应该可以很容易地转换为 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”或“数组迭代”。

让我们为您联系Bird专家。
在30分钟内见证Bird的全部威力。

通过提交,您同意 Bird 可能会就我们的产品和服务与您联系。

您可以随时取消订阅。查看Bird的隐私声明以获取有关数据处理的详细信息。

Newsletter

通过每周更新到您的收件箱,随时了解 Bird 的最新动态。

让我们为您联系Bird专家。
在30分钟内见证Bird的全部威力。

通过提交,您同意 Bird 可能会就我们的产品和服务与您联系。

您可以随时取消订阅。查看Bird的隐私声明以获取有关数据处理的详细信息。

Newsletter

通过每周更新到您的收件箱,随时了解 Bird 的最新动态。

让我们为您联系Bird专家。
在30分钟内见证Bird的全部威力。

通过提交,您同意 Bird 可能会就我们的产品和服务与您联系。

您可以随时取消订阅。查看Bird的隐私声明以获取有关数据处理的详细信息。

R

Reach

G

Grow

M

Manage

A

Automate

Newsletter

通过每周更新到您的收件箱,随时了解 Bird 的最新动态。