TIdMessage - 附件在正文中显示为 Base64
TIdMessage - Attachments Show Up in Body as Base64
我正在使用 Indy 组件 (TIdMessage
) 通过 SMTP 发送电子邮件。这样的邮件需要HTML,需要带附件
现在,如果我以纯文本形式发送电子邮件 (ContentType := 'text/plain'
),并附加一个文件,电子邮件会正常发送,找到附件等等。
然而,一旦我将 ContentType
更改为 text/html
,我得到了一个非常奇怪的结果。电子邮件的整个正文显然被底层电子邮件数据替换(用我的话来说),并将正文中的附件显示为 Base64 数据。
例如,这只是生成的电子邮件的前几行:
This is a multi-part message in MIME format
--YGuFowdSjNaa=_khosBzZl5L8uGVtfasBX
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
This is the body of a test email.
--YGuFowdSjNaa=_khosBzZl5L8uGVtfasBX
Content-Type: application/octet-stream; name="TagLogo.jpg"
Content-Transfer-Encoding: base64
Content-Disposition: inline; filename="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
为什么这会导致 "garbage" 电子邮件,如何在支持带附件的 HTML 电子邮件正文的同时解决这个问题?
PS - 如果有任何区别,附件最终将是电子邮件正文中内联使用的图像。
内容类型应该是multipart/mixed。
Such an email needs to be HTML, and needs to carry attachments
我在 Indy 的网站上有关于这个主题的博客文章,我建议您阅读它们:
New HTML Message Builder class
Why is this resulting in a "garbage" email
简而言之,因为您没有正确填充 TIdMessage
。
在您的特定示例中,您将 HTML 放入 TIdMessage.Body
属性,但您还向 TIdMessage.MessageParts
collection 添加了一个项目].该组合在 TIdMessageClient
(TIdSMTP
派生自)内部有一些特殊处理。特别是,当:
TIdMessage
是 MIME-encoded,
- 且
TIdMessage.IsMsgSinglePartMime
为假,
- 和
TIdMessage.IsBodyEmpty()
returns false(当TIdMessage.Body
包含non-whitespace文本时),
- 和
TIdMessage.ConvertPreamble
为真(默认情况下为真),
- 并且
TIdMessage.MessageParts
collection 不为空,并且其中没有 TIdText
object。
然后TIdMessageClient.SendBody()
将HTML移动到MessageParts
collection中新的TIdText
object,并生成一个MIME-encoded 将 TIdMessage.Body
文本作为第一个 MIME 部分的电子邮件 body。假设在这种组合中,用户无意中将消息 plain-text 放入 TIdMessage.Body
中,因此 Indy 将其移动到需要进一步处理的位置,而不更改电子邮件中的任何其他内容。然而,这意味着 TIdMessage.ContentType
属性 没有调整(可能是一个应该调查的错误)。在您的情况下,您将 ContentType
设置为 text/html
当它确实需要 MIME multipart/...
类型时(取决于附件与 HTML 的关系) .
所以,您实际上是在发送这样一封电子邮件:
Content-Type: text/html; charset=us-ascii; boundary="AduWRpEOzrMvJDhg6Jp8EsEFw5Qr1p=_1v"
MIME-Version: 1.0
Date: Tue, 8 Aug 2017 12:58:00 -0700
This is a multi-part message in MIME format
--AduWRpEOzrMvJDhg6Jp8EsEFw5Qr1p=_1v
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
This is the body of a test email.
--AduWRpEOzrMvJDhg6Jp8EsEFw5Qr1p=_1v
Content-Type: application/octet-stream;
name="SomeFile.jpg"
Content-Transfer-Encoding: base64
Content-Disposition: inline;
filename="SomeFile.jpg"
<base64 data here>
--AduWRpEOzrMvJDhg6Jp8EsEFw5Qr1p=_1v--
这告诉电子邮件的接收者 整个 电子邮件是 HTML 而实际上不是。它实际上是多种 MIME 类型的混合。由于您希望图像位于 HTML 内,因此 top-level Content-Type
header 需要改为 multipart/related
(我在我的博客文章中进行了更详细的介绍解释为什么会这样)。
how do I solve it while supporting HTML email body with attachments?
If it makes any difference, the attachments will be images used in-line in the email body in the end.
处理TIdMessage
时,最好是:
只填充 TIdMessage.Body
本身,忽略 TIdMessage.MessageParts
collection.
把所有的东西都放在TIdMessage.MessageParts
collection,正文什么的,忽略TIdMessage.Body
属性.
在这种情况下,由于您需要附件,因此需要使用 MessageParts
collection,因此您可以:
自己把HTML放在TIdText
objectTIdMessage.MessageParts
collection里面(不要让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';
或者,如果您想为 non-HTML 读者提供 plain-text 消息,您需要将 HTML 和 plain-text(按此顺序)包装在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
class,让它为你配置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) ...
我正在使用 Indy 组件 (TIdMessage
) 通过 SMTP 发送电子邮件。这样的邮件需要HTML,需要带附件
现在,如果我以纯文本形式发送电子邮件 (ContentType := 'text/plain'
),并附加一个文件,电子邮件会正常发送,找到附件等等。
然而,一旦我将 ContentType
更改为 text/html
,我得到了一个非常奇怪的结果。电子邮件的整个正文显然被底层电子邮件数据替换(用我的话来说),并将正文中的附件显示为 Base64 数据。
例如,这只是生成的电子邮件的前几行:
This is a multi-part message in MIME format --YGuFowdSjNaa=_khosBzZl5L8uGVtfasBX Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline This is the body of a test email. --YGuFowdSjNaa=_khosBzZl5L8uGVtfasBX Content-Type: application/octet-stream; name="TagLogo.jpg" Content-Transfer-Encoding: base64 Content-Disposition: inline; filename="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
为什么这会导致 "garbage" 电子邮件,如何在支持带附件的 HTML 电子邮件正文的同时解决这个问题?
PS - 如果有任何区别,附件最终将是电子邮件正文中内联使用的图像。
内容类型应该是multipart/mixed。
Such an email needs to be HTML, and needs to carry attachments
我在 Indy 的网站上有关于这个主题的博客文章,我建议您阅读它们:
New HTML Message Builder class
Why is this resulting in a "garbage" email
简而言之,因为您没有正确填充 TIdMessage
。
在您的特定示例中,您将 HTML 放入 TIdMessage.Body
属性,但您还向 TIdMessage.MessageParts
collection 添加了一个项目].该组合在 TIdMessageClient
(TIdSMTP
派生自)内部有一些特殊处理。特别是,当:
TIdMessage
是 MIME-encoded,- 且
TIdMessage.IsMsgSinglePartMime
为假, - 和
TIdMessage.IsBodyEmpty()
returns false(当TIdMessage.Body
包含non-whitespace文本时), - 和
TIdMessage.ConvertPreamble
为真(默认情况下为真), - 并且
TIdMessage.MessageParts
collection 不为空,并且其中没有TIdText
object。
然后TIdMessageClient.SendBody()
将HTML移动到MessageParts
collection中新的TIdText
object,并生成一个MIME-encoded 将 TIdMessage.Body
文本作为第一个 MIME 部分的电子邮件 body。假设在这种组合中,用户无意中将消息 plain-text 放入 TIdMessage.Body
中,因此 Indy 将其移动到需要进一步处理的位置,而不更改电子邮件中的任何其他内容。然而,这意味着 TIdMessage.ContentType
属性 没有调整(可能是一个应该调查的错误)。在您的情况下,您将 ContentType
设置为 text/html
当它确实需要 MIME multipart/...
类型时(取决于附件与 HTML 的关系) .
所以,您实际上是在发送这样一封电子邮件:
Content-Type: text/html; charset=us-ascii; boundary="AduWRpEOzrMvJDhg6Jp8EsEFw5Qr1p=_1v" MIME-Version: 1.0 Date: Tue, 8 Aug 2017 12:58:00 -0700 This is a multi-part message in MIME format --AduWRpEOzrMvJDhg6Jp8EsEFw5Qr1p=_1v Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline This is the body of a test email. --AduWRpEOzrMvJDhg6Jp8EsEFw5Qr1p=_1v Content-Type: application/octet-stream; name="SomeFile.jpg" Content-Transfer-Encoding: base64 Content-Disposition: inline; filename="SomeFile.jpg" <base64 data here> --AduWRpEOzrMvJDhg6Jp8EsEFw5Qr1p=_1v--
这告诉电子邮件的接收者 整个 电子邮件是 HTML 而实际上不是。它实际上是多种 MIME 类型的混合。由于您希望图像位于 HTML 内,因此 top-level Content-Type
header 需要改为 multipart/related
(我在我的博客文章中进行了更详细的介绍解释为什么会这样)。
how do I solve it while supporting HTML email body with attachments?
If it makes any difference, the attachments will be images used in-line in the email body in the end.
处理TIdMessage
时,最好是:
只填充
TIdMessage.Body
本身,忽略TIdMessage.MessageParts
collection.把所有的东西都放在
TIdMessage.MessageParts
collection,正文什么的,忽略TIdMessage.Body
属性.
在这种情况下,由于您需要附件,因此需要使用 MessageParts
collection,因此您可以:
自己把HTML放在
TIdText
objectTIdMessage.MessageParts
collection里面(不要让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';
或者,如果您想为 non-HTML 读者提供 plain-text 消息,您需要将 HTML 和 plain-text(按此顺序)包装在
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
class,让它为你配置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) ...