我正在使用Delphi 10.2 Tokyo和Indy 10.6.2.5366。
我正在尝试从外部系统接收一些SMTP消息,并且收到的消息使用多部分MIME。我需要从消息中提取一些字段和值。
当我在Indy SMTPServer Demo中读取正文时,正文为空,但正文被写入保存的文件。
如何将该身体读入TStringList
?这是我必须开发的手册,还是有一些方法(找不到一个)来做这个?
这就是我的所作所为(灵感来自演示):
procedure TForm1.IdSMTPServer1MsgReceive(ASender: TIdSMTPServerContext;
AMsg: TStream; var LAction: TIdDataReply);
var
LMsg : TIdMessage;
LMsg2 : TIdMessage;
LStream : TFileStream;
i : integer;
AMsgpart : TidMessagePart;
begin
// When a message is received by the server, this event fires.
// The message data is made available in the AMsg : TStream.
// In this example, we will save it to a temporary file, and the load it using
// IdMessage and parse some header elements.
AddToLog('Msg recv '+formatdatetime('hh:nn:ss', Now));
LMsg2 := TIdMessage.Create;
try
LMsg2.LoadFromStream(AMsg);
ToLabel.Caption := LMsg2.Recipients.EMailAddresses;
FromLabel.Caption := LMsg2.From.Text;
SubjectLabel.Caption := LMsg2.Subject;
// not working
// Memo1.Lines := LMsg2.Body;
// so what to do with this multi-part message in MIME format.
for i := 0 to LMsg2.MessageParts.Count-1 do
begin
if (LMsg2.MessageParts.Items[i].PartType = mptText) then
begin
//AddToLog(LMsg2.MessageParts.Items[i].);
AMsgpart := LMsg2.MessageParts.Items[i];
end
end;
finally
FreeAndNil(LMsg2);
end;
// Just write to a file as in demo
LStream := TFileStream.Create(ExtractFilePath(Application.exename) + format('test(%d).eml',[mailno]), fmCreate);
Try
LStream.CopyFrom(AMsg, 0);
Finally
FreeAndNil(LStream);
End;
end;
写入文件时的消息流如下所示
Received: from WIN-2SP97MPF39L[192.168.10.141] (helo=DesigoCC1) by HPNB2.hnit.local[192.168.10.131] with esmtp (My SMTP Server)
From: Alarms@DCC.dk
Date: Fri, 02 Feb 2018 09:46:39 +0100
To: mail@mail.com
Subject: =?UTF-8?B?QWxhcm0=?=
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="----=_NextPart_000_0006_01CAB9FA.E6640E80"
This is a multi-part message in MIME format.
------=_NextPart_000_0006_01CAB9FA.E6640E80
Content-Type: text/plain;
format=flowed;
charset="utf-8";
reply-type=original
Content-Transfer-Encoding: 7bit
Date:2/2/2018 9:45:03 AM:02/02 09:45
Category:Information:
Desc:Servers.Main Server:
DescCompl:Project.Management System.Servers.Main Server
Tag:Mail.SMTP Status
TagCompl:Mail.SMTP Status [Mail]
Status:Quiet
EventComplProject.Management System.Servers.Main Server
Message:N/A
Cause:Mail Error (Wrong SMTP Server):Mail Error
-----
Message ID: -(6138661-
------=_NextPart_000_0006_01CAB9FA.E6640E80--
答案 0 :(得分:1)
TStream
事件提供的OnMsgReceive
是远程客户端发送的原始电子邮件数据。但是,它不是TIdMessage.LoadFromStream()
默认的格式。具体来说,TIdMessage.LoadFrom...()
方法期望数据采用SMTP在传输过程中使用的转义格式。在OnMsgReceive
事件被触发之前,该撤消将被撤消。因此,按原样加载TStream
可能导致解析问题。
幸运的是,Indy有一个IdMessageHelper
单元来解决这个问题(请参阅New TIdMessage helper,其中更详细地描述了该问题)。它引入了新的LoadFrom...()
方法,它们添加了一个AUsesDotTransparency
参数,您可以将其设置为False,这样TIdMessage
在解析时就不会再期望转义了。
对于电子邮件的内容,多部分MIME正文不存储在TIdMessage.Body
属性中,它们存储在TIdMessagePart
派生对象中,如TIdText
, TIdMessage.MessageParts
属性。因此,您需要浏览该属性以查找您感兴趣的文本。
您还必须记住,Indy的大多数基于TCP的服务器都是多线程的。 OnMsgReceive
事件在工作线程中触发,因此如果要访问UI,则必须与主UI线程同步。
话虽如此,尝试更像这样的事情:
uses
..., IdGlobalProtocols, IdMessageParts, IdText, IdMessage, IdMessageHelper;
procedure TForm1.IdSMTPServer1MsgReceive(ASender: TIdSMTPServerContext;
AMsg: TStream; var LAction: TIdDataReply);
var
LMsg : TIdMessage;
LStream : TFileStream;
i : integer;
LMsgPart : TIdMessagePart;
LText : TIdText;
begin
// When a message is received by the server, this event fires.
// The message data is made available in the AMsg : TStream.
AddToLog('Msg recv ' + FormatDateTime('hh:nn:ss', Now));
LMsg := TIdMessage.Create;
try
//LMsg.LoadFromStream(AMsg);
LMsg.LoadFromStream(AMsg, False);
TThread.Synchronize(nil,
procedure
begin
ToLabel.Caption := LMsg.Recipients.EMailAddresses;
// FYI, the *true* recipients of the email are stored in the
// ASender.RCPTList property, which may include additional
// recipients not specified by TIdMessage.Recipients due to BCCs...
FromLabel.Caption := LMsg.From.Text;
// FYI, ASender also has a From property, which is the *true* sender
// of the email, which may be different than what TIdMessage.From
// says...
SubjectLabel.Caption := LMsg.Subject;
end
);
if IsHeaderMediaType(LMsg.ContentType, 'multipart') then
begin
LText := nil;
for i := 0 to LMsg.MessageParts.Count-1 do
begin
LMsgPart := LMsg.MessageParts[i];
if (LMsgPart is TIdText) and
IsHeaderMediaType(LMsgPart.ContentType, 'text/plain') then
begin
//AddToLog(LMsgPart);
LText := TIdText(LMsgPart);
Break;
end
end;
TThread.Synchronize(nil,
procedure
begin
if LText <> nil then
Memo1.Lines := LText.Body
else
Memo1.Clear;
end
);
end
else if IsHeaderMediaType(LMsg.ContentType, 'text') then
begin
TThread.Synchronize(nil,
procedure
begin
Memo1.Lines := LMsg.Body;
end
);
end else
begin
TThread.Synchronize(nil,
procedure
begin
Memo1.Clear;
end
);
end;
finally
LMsg.Free;
end;
// Just write to a file as in demo
LStream := TFileStream.Create(ExtractFilePath(Application.ExeName) + Format('test(%d).eml', [mailno]), fmCreate);
try
LStream.CopyFrom(AMsg, 0);
finally
LStream.Free;
end;
// Alternatively, AMsg is a TMemoryStream by default, unless you
// provide your own TStream in the OnBeforeMsg event...
//
// TMemoryStream(AMsg).SaveToFile(ExtractFilePath(Application.ExeName) + Format('test(%d).eml', [mailno]));
end;