TIdMessage - 附件在正文中显示为 Base64

Posted

技术标签:

【中文标题】TIdMessage - 附件在正文中显示为 Base64【英文标题】:TIdMessage - Attachments Show Up in Body as 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 - 如果有任何不同,附件将是最终在电子邮件正文中内嵌使用的图像。

【问题讨论】:

【参考方案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 集合,因此您可以:

    自己将 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.';
    

    使用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 实际上是通过一些特殊处理遇到了一个极端情况。我更新了答案以解释正在发生的事情。

以上是关于TIdMessage - 附件在正文中显示为 Base64的主要内容,如果未能解决你的问题,请参考以下文章

解决java mail发送TXT附件被直接显示在正文中的问题

在电子邮件正文中显示附加图像

如何删除图像作为附件但显示在电子邮件正文中

文本文件在ios目标c的邮件正文中显示为一个小框

Rspec 不显示邮件正文中的内容

UiPath Outlook邮件正文引用图片