【问题标题】:TIdMessage - Attachments Show Up in Body as Base64TIdMessage - 附件在正文中显示为 Base64
【发布时间】:2018-01-16 10:05:37
【问题描述】:

我正在使用 Indy 组件 (TIdMessage) 通过 SMTP 发送电子邮件。这样的邮件需要是HTML,并且需要携带附件。

现在,如果我以纯文本 (ContentType := 'text/plain') 形式发送电子邮件并附上文件,则电子邮件会正常发送,找到附件以及所有内容。

但是,一旦我将ContentType 更改为text/html,我就会得到一个非常奇怪的结果。电子邮件的整个正文显然被底层电子邮件数据(用我的话)替换,并将正文中的附件显示为 Base64 数据。

例如,这只是生成的电子邮件的前几行:

这是一条 MIME 格式的多部分消息 --YGuFowdSjNaa=_khosBzZl5L8uGVtfasBX 内容类型:文本/纯文本; charset="us-ascii" 内容传输编码:quoted-printable 内容处置:内联 这是测试电子邮件的正文。 --YGuFowdSjNaa=_khosBzZl5L8uGVtfasBX 内容类型:应用程序/八位字节流;名称="标签标志.jpg" 内容传输编码:base64 内容处置:内联;文件名="TagLogo.jpg" /9j/4AAQSkZJRgABAQEASwBLAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcU FhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgo KCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCADhAG8DASIA AhEBAxEB/8QAHAAAAgMBAQEBAAAAAAAAAAAAAAcFBggEAgED/8QAPxAAAQIFAwEEBwYFAwQDAAAA AQIDAAQFBhEHEiExCBNBdSI3OFFhsrMUMkJxgZEVIzNSgmJyoRaSosEk0eH/xAAWAQEBAQAA

虽然原来的身体只是

This is the body of a test email.

代码很简单,符合我在网上能找到的所有例子……

IdMessage.Charset := 'UTF-8';
IdMessage.ContentType := 'text/html'; // <-- text/plain works fine.....
IdMessage.Body := 'This is the body of a test email.';

... (Assigning Other Unrelated Properties) ...

A := TIdAttachmentFile.Create(IdMessage.MessageParts, 'C:\SomeFile.jpg'); // <-- Originally the only LOC here
A.ContentTransfer := 'base64'; // <-- Tried with and without
A.ContentType := 'application/octet-stream'; // <-- Tried with and without
//A.ContentType := 'image/jpeg'; // <-- Tried with and without
A.ContentDisposition := 'inline'; // <-- Tried with and without

为什么这会导致“垃圾”电子邮件,在支持带有附件的 HTML 电子邮件正文的同时如何解决它?

PS - 如果有任何不同,附件将是最终在电子邮件正文中内嵌使用的图像。

【问题讨论】:

    标签: html delphi smtp email-attachments indy


    【解决方案1】:

    内容类型应为multipart/mixed

    【讨论】:

    • 刚试过,虽然附件没问题,但正文不是 HTML(与 text/plain 没有什么不同)。这部分很重要……
    • 实际上,对于嵌入在 HTML 中的图像,HTML 和附件需要包裹在 multipart/related; type="text/html" 中,并且需要额外设置才能将附件实际链接到 HTML。
    【解决方案2】:

    这样的邮件需要是HTML,并且需要携带附件

    我在 Indy 的网站上有关于这个主题的博客文章,我建议你阅读它们:

    HTML Messages

    New HTML Message Builder class

    为什么这会导致“垃圾”电子邮件

    简而言之,因为您没有正确填充TIdMessage

    在您的特定示例中,您将 HTML 放入 TIdMessage.Body 属性中,但您还将项目添加到 TIdMessage.MessageParts 集合中。该组合在TIdMessageClientTIdSMTP 派生自)内部有一些特殊处理。特别是,当:

    • TIdMessage 将被 MIME 编码,
    • TIdMessage.IsMsgSinglePartMime 是假的,
    • TIdMessage.IsBodyEmpty() 返回false(当TIdMessage.Body 包含非空白文本时),
    • 并且TIdMessage.ConvertPreamble 为真(默认为真),
    • 并且TIdMessage.MessageParts 集合不为空,并且其中没有TIdText 对象。

    然后TIdMessageClient.SendBody() 会将 HTML 移动到 MessageParts 集合中的新 TIdText 对象,并生成一个 MIME 编码的电子邮件正文,其中 TIdMessage.Body 文本作为第一个 MIME 部分。假设在这种组合中,用户无意中将消息纯文本放在TIdMessage.Body 中,因此 Indy 将其移动到需要进一步处理的位置,而不更改电子邮件中的任何其他内容。然而,这意味着 TIdMessage.ContentType 属性没有被调整(可能是一个应该调查的错误)。在您的情况下,您将 ContentType 设置为 text/html,而它确实需要是 MIME multipart/... 类型(取决于附件与 HTML 的关系)。

    因此,您实际上是在发送这样的电子邮件:

    内容类型:文本/html;字符集=我们-ASCII;边界="AduWRpEOzrMvJDhg6Jp8EsEFw5Qr1p=_1v" MIME 版本:1.0 日期:2017 年 8 月 8 日星期二 12:58:00 -0700 这是一条 MIME 格式的多部分消息 --AduWRpEOzrMvJDhg6Jp8EsEFw5Qr1p=_1v 内容类型:文本/纯文本; charset="us-ascii" 内容传输编码:quoted-printable 内容处置:内联 这是测试电子邮件的正文。 --AduWRpEOzrMvJDhg6Jp8EsEFw5Qr1p=_1v 内容类型:应用程序/八位字节流; 名称="SomeFile.jpg" 内容传输编码:base64 内容处置:内联; 文件名="SomeFile.jpg" --AduWRpEOzrMvJDhg6Jp8EsEFw5Qr1p=_1v--

    它告诉电子邮件的接收者整个电子邮件是 HTML,而实际上不是。它实际上是多种 MIME 类型一起工作的混合体。由于您希望图像位于 HTML 中,因此顶级 Content-Type 标头需要改为 multipart/related(我将在我的博客文章中详细说明原因)。

    在支持带有附件的 HTML 电子邮件正文时如何解决?

    如果有任何不同,附件最终将是在电子邮件正文中内嵌使用的图像。

    TIdMessage打交道时,最好是:

    • 仅填充 TIdMessage.Body 本身,并忽略 TIdMessage.MessageParts 集合。

    • 将所有内容放入TIdMessage.MessageParts 集合、文本和所有内容,并忽略TIdMessage.Body 属性。

    在这种情况下,由于您需要附件,因此您需要使用MessageParts 集合,因此您可以:

    1. 自己将 HTML 放入 TIdMessage.MessageParts 集合中的 TIdText 对象中(不要让 TIdMessageClient 为你做),然后将 TIdMessage.ContentType 设置为 multipart/related

      IdMessage.ContentType := 'multipart/related; type="text/html"';
      ... (Assigning Other Unrelated Properties) ...
      
      T := TIdText.Create(IdMessage.MessageParts, nil);
      T.ContentType := 'text/html';
      T.Charset := 'utf-8';
      T.Body.Text := '<html><body>This is the body of a test email.<p><img src="cid:myimage"></body></html>';
      
      A := TIdAttachmentFile.Create(IdMessage.MessageParts, 'C:\SomeFile.jpg');
      A.ContentTransfer := 'base64';
      A.ContentType := 'image/jpeg';
      A.ContentDisposition := 'inline';
      A.ContentID := 'myimage';
      

      或者,如果您想为非 HTML 阅读器提供纯文本消息,则需要将 HTML 和纯文本(按此顺序)包装在 multipart/alternative 内,同时保留 HTML 和图像附件multipart/related内部:

      IdMessage.ContentType := 'multipart/alternative';
      ... (Assigning Other Unrelated Properties) ...
      
      T := TIdText.Create(IdMessage.MessageParts, nil);
      T.ContentType := 'multipart/related; type="text/html"';
      
      T := TIdText.Create(IdMessage.MessageParts, nil);
      T.ContentType := 'text/html';
      T.Charset := 'utf-8';
      T.Body.Text := '<html><body>This is the body of a test email.<p><img src="cid:myimage"></body></html>';
      T.ParentPart := 0;
      
      A := TIdAttachmentFile.Create(IdMessage.MessageParts, 'C:\SomeFile.jpg');
      A.ContentTransfer := 'base64';
      A.ContentType := 'image/jpeg';
      A.ContentDisposition := 'inline';
      A.ContentID := 'myimage';
      A.ParentPart := 0;
      
      T := TIdText.Create(IdMessage.MessageParts, nil);
      T.ContentType := 'text/plain';
      T.Charset := 'utf-8';
      T.Body.Text := 'This is the body of a test email.';
      
    2. 使用TIdMessageBuilderHtml类,让它为你配置TIdMessage内容:

      uses
        ..., IdMessageBuilder;
      
      MB := TIdMessageBuilderHtml.Create;
      try
        // optional...
        MB.PlainText.Text := 'This is the body of a test email.';
        MB.PlainTextCharSet := 'utf-8';
      
        MB.Html.Text := '<html><body>This is the body of a test email.<p><img src="cid:myimage"></body></html>';
        MB.HtmlCharSet := 'utf-8';
        MB.HtmlFiles.Add('C:\SomeFile.jpg', 'myimage');
      
        MB.FillMessage(IdMessage);
      finally
        MB.Free;
      end;
      
      ... (Assigning Other Unrelated Properties) ...
      

    【讨论】:

    • 我想我只是假设由于每个附件都有自己的内容类型,它会立即知道其中的区别,但这种假设是错误的。
    • @JerryDodge:在某种程度上,确实如此。但是在电子邮件生成过程中发生了很多事情,OP 实际上是通过一些特殊处理遇到了一个极端情况。我更新了答案以解释正在发生的事情。
    猜你喜欢
    • 2019-02-08
    • 1970-01-01
    • 1970-01-01
    • 2012-09-07
    • 2018-12-23
    • 1970-01-01
    • 2020-10-15
    • 2016-08-02
    • 2019-12-14
    相关资源
    最近更新 更多