如何正确通过电子邮件发送嵌入的Base64图像(HTML)?

huangapple go评论69阅读模式
英文:

How to send embedded base64 image (HTML) by email correctly?

问题

我想使用Indy来发送带有嵌入图像的电子邮件,对于这些情况,HTML模板必须具有base64转换的图像。

示例HTML模板:

<html>
  <head>
  </head>
  <body>
    <div>
      <p>Some text</p>
      <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4
        //8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />
    </div>
  </body>
</html>

这个HTML只是用于测试,但即使是这个简单的base64图像和纯文本,当我使用Indy发送它时,我没有正确接收图像。我收到HTML代码,或带有损坏图像的文本,或者图像根本不加载(带有空白空间)。

但是,当我在常见的浏览器中打开HTML文件(例如Chrome或Firefox),图像可以正常加载。

我尝试了以下例程:

uses
  idMessage, idText, IdSMTP, IdSSLOpenSSL, IdExplicitTLSClientServerBase;

procedure SendMail;
var
  html: TStringList;
  email: TIdMessage;
  idSMTP: TIdSMTP;
  idSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
  html := TStringList.Create;
  html.LoadFromFile('<my_html_file>');

  email := TIdMessage.Create(nil);
  email.From.Text := 'from@mail.com';
  email.From.Name := 'from name';
  email.Recipients.EMailAddresses := 'recipient';

  email.Subject := 'From DELPHI';
  email.ContentType := 'multipart/mixed';  // 电子邮件带有HTML文本
  // email.ContentType := 'text/html';  // 电子邮件带有纯文本,但没有图像
  email.Body.Assign(html);

  // SSL配置 //
  idSSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  idSSL.SSLOptions.Mode := sslmClient;
  idSSL.SSLOptions.Method := sslvSSLv23;

  // SMTP配置 //
  idSMTP := TIdSMTP.Create(nil);
  idSMTP.IOHandler := idSSL;
  idSMTP.Host := 'smtp.office365.com';
  idSMTP.Port := 587;
  idSMTP.AuthType := satDefault;
  // idSMTP.UseTLS := utUseImplicitTLS;
  idSMTP.UseTLS := utUseExplicitTLS;
  idSMTP.Username := 'mail@mail.com';
  idSMTP.Password := 'pass';

  try
    idSMTP.Connect();
    idSMTP.Send(email);
    ShowMessage('已发送');
  except
    on E: Exception do
      ShowMessage('失败:' + E.Message);
  end;
end;

我还尝试使用TIdMessageBuilderHtml,但在这种情况下没有成功。

我做错了什么?

英文:

I want to use Indy to send emails with embedded images, and for those cases the HTML template must have the base64 converted image.

Sample HTML template:

&lt;html&gt;
  &lt;head&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div&gt;
      &lt;p&gt;Some text&lt;/p&gt;
      &lt;img src=&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4
        //8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==&quot; alt=&quot;Red dot&quot; /&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;

This HTML is just for testing, but even with this simple base64 image and plain text, when I send it via email with Indy, I don't receive the image correctly. I receive the HTML code, or the text with a broken image, or the image don't even load (comes with a blank space).

BUT, when I open the HTML file in a common browser (ie, Chrome or Firefox), the image loads without problem.

I've tried the following routine:

uses
  idMessage, idText, IdSMTP, IdSSLOpenSSL, IdExplicitTLSClientServerBase;

procedure SendMail;
var
  html: TStringList;
  email: TIdMessage;
  idSMTP: TIdSMTP;
  idSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
  html:= TStringlist.Create;
  html.LoadFromFile(&#39;&lt;my_html_file&gt;&#39;);

  email := TIdMessage.Create(nil);
  email.From.Text := &#39;from@mail.com&#39;;
  email.From.Name:= &#39;from name&#39;; ///
  email.Recipients.EMailAddresses := &#39;recipient&#39;;

  email.Subject := &#39;From DELPHI&#39;;
  email.ContentType := &#39;multipart/mixed&#39;;  //email comes with HTML text
  //email.ContentType := &#39;text/html&#39;;  //email comes with plain text, but not images
  email.Body.Assign(html);

  // SSL stuff //
  idSSL:= TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  idSSL.SSLOptions.Mode:= sslmClient;
  idSSL.SSLOptions.Method:= sslvSSLv23;

  // SMTP stuff //
  idSMTP:= TIdSMTP.Create(nil);
  idSMTP.IOHandler:= idSSL;
  idSMTP.Host:= &#39;smtp.office365.com&#39;;
  idSMTP.Port:=  587;
  idSMTP.AuthType := satDefault;
  //idSMTP.UseTLS:= utUseImplicitTLS;
  idSMTP.UseTLS:= utUseExplicitTLS;
  idSMTP.Username:= &#39;mail@mail.com&#39;;
  idSMTP.Password:=  &#39;pass&#39;;

  try
    idSMTP.Connect();
    idSMTP.Send(email);
    ShowMessage(&#39;Sent&#39;);
  except
    on E: Exception do
    ShowMessage(&#39;Failed: &#39; + E.Message);
  end;
end;

I also tried to use TIdMessageBuilderHtml, but without success on this case.

What am I doing wrong?

答案1

得分: 0

以下是您要翻译的内容:

"Generic solution for using images in html part of email it’s build in images as attachment and use attachment ID as SRC of image.
something like:

<img src="cid:2.jpg" alt="Red dot" />

In this case all client mail viewers must show email as it's should be.

Here is part of my old project that add all html images into attachments from 'file names' into corrent identifiers. It's not optimal sourses but you can get main idia from it.

uses
IdBaseComponent,
IdComponent, IdTCPConnection, IdTCPClient, IdExplicitTLSClientServerBase,
IdMessageClient, IdSMTPBase, IdSMTP, IdIOHandler, IdIOHandlerSocket,
IdIOHandlerStack, IdSSL, IdSSLOpenSSL,
idMessage, IdAttachment,
idText, IdAttachmentFile, math;

function HtmlDecode (const AStr: String): String;
begin
Result := StringReplace(AStr, '&apos;', '''', [rfReplaceAll]); {Do not Localize}
Result := StringReplace(Result, '&quot;', '"', [rfReplaceAll]); {Do not Localize}
Result := StringReplace(Result, '&gt;', '>', [rfReplaceAll]); {Do not Localize}
Result := StringReplace(Result, '&lt;', '<', [rfReplaceAll]); {Do not Localize}
Result := StringReplace(Result, '&amp;', '&', [rfReplaceAll]); {Do not Localize}
end;

function IsXDigit(Ch : char) : Boolean;
begin
Result := (ch in ['0'..'9']) or (ch in ['a'..'f']) or (ch in ['A'..'F']);
end;

function XDigit(Ch : char) : Integer;
begin
if ch in ['0'..'9'] then
Result := ord(Ch) - ord('0')
else
Result := (ord(Ch) and 15) + 9;
end;

function htoin(value : PChar; len : Integer) : Integer;
var
i : Integer;
begin
Result := 0;
i := 0;
while (i < len) and (Value[i] = ' ') do
i := i + 1;
while (i < len) and (isxDigit(Value[i])) do begin
Result := Result * 16 + xdigit(Value[i]);
i := i + 1;
end;
end;

function htoi2(value : PChar) : Integer;
begin
Result := htoin(value, 2);
end;

function UrlDecode(S : String) : String;
var
I : Integer;
Ch : Char;
begin
Result := '';
I := 1;
while (I <= Length(S)) do begin
Ch := S[I];
if Ch = '%' then begin
Ch := chr(htoi2(@S[I + 1]));
Inc(I, 2);
end
else if Ch = '+' then
Ch := ' ';
Result := Result + Ch;
Inc(I);
end;
end;

procedure TForm3.HandleIMGID(ASourceDir: String;
var AHTMString: String; AMessage: TIDMessage; AParentPart : integer = -1);
var
IMGPos : Integer ;
CurrentPoint : Pchar ;
AFile : String ;
AAttachment : TIdAttachment ;
begin
CurrentPoint := pchar(AHTMString);
IMGPos := 0 ;
while pos('src="', CurrentPoint) <> 0 do begin
IMGPos := IMGPos + pos('src="',CurrentPoint) + 4 ;
CurrentPoint := pchar(AHTMString) + IMGPos ;
if pos('"', CurrentPoint) <> 0 then begin
AFile := copy(AHTMString, IMGPos + 1, pos('"', CurrentPoint) - 1);
AFile := UrlDecode(afile) ;
AFile := StringReplace(AFile, '/', '&#39;, [rfReplaceAll]);
if FileExists(IncludeTrailingBackslash(ASourceDir) + AFile) then
AFile := IncludeTrailingBackslash(ASourceDir) + AFile;

  if FileExists(AFile) then begin
    AAttachment := TIdAttachmentFile.Create(AMessage.MessageParts, AFile);
    AAttachment.FileName := HtmlDecode(ExtractFileName(AFile));
    AAttachment.ContentType := &#39;image/jpeg&#39;;
    AAttachment.Headers.Add(&#39;Content-ID: &lt;&#39; + AAttachment.FileName + &#39;&gt;&#39;);
    AAttachment.ParentPart := AParentPart;
    delete(AHTMString, IMGPos + 1, pos(&#39;&quot;&#39;, CurrentPoint) - 1);
    insert(&#39;cid:&#39; + AAttachment.FileName, AHTMString, IMGPos + 1);
  end{if};
  CurrentPoint := CurrentPoint + min(pos(&#39;&quot;&#39;, CurrentPoint), 0);
  AFile := &#39;&#39;;
end{if};

end{while};
end;

procedure TForm3.Button1Click(Sender: TObject);
var
s : string;
xMessage : TIDMessage;
APlainHTML, APlainText, ATextPart, AEmail : TIdText ;
begin
IdSMTP1.Username := 'from@mail.com';
IdSMTP1.Password := 'password';
IdSMTP1.Port := 587;
IdSMTP1.Host := 'hosturl.com';

IdSMTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
IdSMTP1.UseTLS := utUseExplicitTLS;
IdSMTP1.Connect;

xMessage := TIDMessage.Create(self) ;
try
//fill message attributes
xMessage.From.Name := 'from@mail.com';
xMessage.From.Address := 'from@mail.com';
xMessage.Subject := 'Some test subject';

//fill Recipients
xMessage.Recipients.Clear ;

xMessage.Recipients.EMailAddresses := &#39
英文:

Generic solution for using images in html part of email it’s build in images as attachment and use attachment ID as SRC of image.
something like:

&lt;img src=&quot;cid:2.jpg&quot; alt=&quot;Red dot&quot; /&gt;

In this case all client mail viewers must show email as it's should be.

Here is part of my old project that add all html images into attachments from 'file names' into corrent identifiers. It's not optimal sourses but you can get main idia from it.

uses
  IdBaseComponent,
  IdComponent, IdTCPConnection, IdTCPClient, IdExplicitTLSClientServerBase,
  IdMessageClient, IdSMTPBase, IdSMTP, IdIOHandler, IdIOHandlerSocket,
  IdIOHandlerStack, IdSSL, IdSSLOpenSSL,
  idMessage, IdAttachment,
  idText, IdAttachmentFile, math;

function HtmlDecode (const AStr: String): String;
begin
	Result := StringReplace(AStr,   &#39;&amp;apos;&#39;, &#39;&#39;&#39;&#39;, [rfReplaceAll]);    {Do not Localize}
	Result := StringReplace(Result, &#39;&amp;quot;&#39;, &#39;&quot;&#39;, [rfReplaceAll]);    {Do not Localize}
	Result := StringReplace(Result, &#39;&amp;gt;&#39;, &#39;&gt;&#39;, [rfReplaceAll]);    {Do not Localize}
	Result := StringReplace(Result, &#39;&amp;lt;&#39;, &#39;&lt;&#39;, [rfReplaceAll]);    {Do not Localize}
	Result := StringReplace(Result, &#39;&amp;amp;&#39;, &#39;&amp;&#39;, [rfReplaceAll]);    {Do not Localize}
end;

function IsXDigit(Ch : char) : Boolean;
begin
    Result := (ch in [&#39;0&#39;..&#39;9&#39;]) or (ch in [&#39;a&#39;..&#39;f&#39;]) or (ch in [&#39;A&#39;..&#39;F&#39;]);
end;

function XDigit(Ch : char) : Integer;
begin
    if ch in [&#39;0&#39;..&#39;9&#39;] then
        Result := ord(Ch) - ord(&#39;0&#39;)
    else
        Result := (ord(Ch) and 15) + 9;
end;

function htoin(value : PChar; len : Integer) : Integer;
var
    i : Integer;
begin
  Result := 0;
  i      := 0;
  while (i &lt; len) and (Value[i] = &#39; &#39;) do
      i := i + 1;
  while (i &lt; len) and (isxDigit(Value[i])) do begin
      Result := Result * 16 + xdigit(Value[i]);
      i := i + 1;
  end;
end;

function htoi2(value : PChar) : Integer;
begin
    Result := htoin(value, 2);
end;

function UrlDecode(S : String) : String;
var
    I  : Integer;
    Ch : Char;
begin
    Result := &#39;&#39;;
    I := 1;
    while (I &lt;= Length(S)) do begin
        Ch := S[I];
        if Ch = &#39;%&#39; then begin
            Ch := chr(htoi2(@S[I + 1]));
            Inc(I, 2);
        end
        else if Ch = &#39;+&#39; then
            Ch := &#39; &#39;;
        Result := Result + Ch;
        Inc(I);
    end;
end;

procedure TForm3.HandleIMGID(ASourceDir: String;
  var AHTMString: String; AMessage: TIDMessage; AParentPart : integer = -1);
var
  IMGPos : Integer ;
  CurrentPoint : Pchar ;
  AFile : String ;
  AAttachment : TIdAttachment ;
begin
  CurrentPoint := pchar(AHTMString);
  IMGPos := 0 ;
  while pos(&#39;src=&quot;&#39;, CurrentPoint) &lt;&gt; 0 do begin
    IMGPos := IMGPos + pos(&#39;src=&quot;&#39;,CurrentPoint) + 4 ;
    CurrentPoint := pchar(AHTMString) + IMGPos ;
    if pos(&#39;&quot;&#39;, CurrentPoint) &lt;&gt; 0 then begin
      AFile := copy(AHTMString, IMGPos + 1, pos(&#39;&quot;&#39;, CurrentPoint) - 1);
      AFile := UrlDecode(afile) ;
      AFile := StringReplace(AFile, &#39;/&#39;, &#39;\&#39;, [rfReplaceAll]);
      if FileExists(IncludeTrailingBackslash(ASourceDir) + AFile) then
        AFile := IncludeTrailingBackslash(ASourceDir) + AFile;

      if FileExists(AFile) then begin
        AAttachment := TIdAttachmentFile.Create(AMessage.MessageParts, AFile);
        AAttachment.FileName := HtmlDecode(ExtractFileName(AFile));
        AAttachment.ContentType := &#39;image/jpeg&#39;;
        AAttachment.Headers.Add(&#39;Content-ID: &lt;&#39; + AAttachment.FileName + &#39;&gt;&#39;);
        AAttachment.ParentPart := AParentPart;
        delete(AHTMString, IMGPos + 1, pos(&#39;&quot;&#39;, CurrentPoint) - 1);
        insert(&#39;cid:&#39; + AAttachment.FileName, AHTMString, IMGPos + 1);
      end{if};
      CurrentPoint := CurrentPoint + min(pos(&#39;&quot;&#39;, CurrentPoint), 0);
      AFile := &#39;&#39;;
    end{if};
  end{while};
end;

procedure TForm3.Button1Click(Sender: TObject);
var
  s : string;
  xMessage : TIDMessage;
  APlainHTML, APlainText, ATextPart, AEmail : TIdText ;
begin
  IdSMTP1.Username := &#39;from@mail.com&#39;;
  IdSMTP1.Password := &#39;password&#39;;
  IdSMTP1.Port := 587;
  IdSMTP1.Host := &#39;hosturl.com&#39;;

  IdSMTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
  IdSMTP1.UseTLS := utUseExplicitTLS;
  IdSMTP1.Connect;

  xMessage := TIDMessage.Create(self) ;
  try
    //fill message attributes
    xMessage.From.Name := &#39;from@mail.com&#39;;
    xMessage.From.Address := &#39;from@mail.com&#39;;
    xMessage.Subject := &#39;Some test subject&#39;;

    //fill Recipients
    xMessage.Recipients.Clear ;

    xMessage.Recipients.EMailAddresses := &#39;recipient@gmail.com&#39;;

    xMessage.BccList.Clear;
    xMessage.ccList.Clear;

    //fill message content
    xMessage.ContentType := &#39;multipart/mixed&#39;;
    AEmail := TIdText.create(xMessage.MessageParts);
    AEmail.ContentType := &#39;multipart/related; type=&quot;multipart/alternative&quot;&#39;;

    ATextPart := TIdText.create(xMessage.MessageParts);
    ATextPart.ContentType := &#39;multipart/alternative&#39;;
    ATextPart.ParentPart := 0;

    APlainText :=  TIdText.create(xMessage.MessageParts);
    APlainText.ContentType := &#39;text/plain&#39;;
    APlainText.Body.Text := &#39;Some plain text of mail&#39;;
    APlainText.ParentPart := ATextPart.Index;

    //load html
    APlainHTML := TIdText.create(xMessage.MessageParts);
    APlainHTML.ContentType := &#39;text/html&#39;;
    APlainHTML.ParentPart := ATextPart.Index;
    s :=  &#39;&lt;html&gt;&#39; + #13#10 +
          &#39;  &lt;head&gt;&#39; + #13#10 +
          &#39;  &lt;/head&gt;&#39; + #13#10 +
          &#39;  &lt;body&gt;&#39; + #13#10 +
          &#39;    &lt;div&gt;&#39; + #13#10 +
          &#39;      &lt;p&gt;Some text&lt;/p&gt;&#39; + #13#10 +
//            &#39;      &lt;img src=&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4&#39; + #13#10 +
//            &#39;        //8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==&quot; alt=&quot;Red dot&quot; /&gt;&#39; + #13#10 +
          &#39;      &lt;img src=&quot;2.jpg&quot; alt=&quot;Red dot&quot; /&gt;&#39; + #13#10 +
          &#39;    &lt;/div&gt;&#39; + #13#10 +
          &#39;  &lt;/body&gt;&#39; + #13#10 +
          &#39;&lt;/html&gt;&#39;;
    HandleIMGID(&#39;C:\temp&#39;, s, xMessage, 0);
//    HandleIMGID(FSettings.TeplateFolder, s, xMessage, 0);
    APlainHTML.Body.Text := s;

    if IdSMTP1.Authenticate then begin
      IdSMTP1.Send(xMessage);
    end;
  finally
    xMessage.Free;
  end;
end;

P.S. All images from attachments will not be shown as attached files if it's uses in html part.

答案2

得分: 0

我之前提到我的想法与@Oleksandr Morozevych的答案类似,我基本上将正文中的所有图像从base64转换为临时二进制(图像)文件,然后将其附加到邮件消息中,并且正文&lt;img src=&quot;data:image/png;base64,iVBORw0KGgoAAAAN...会被替换为&lt;img src=&quot;cid:cid_image_id.jpg&quot; /&gt;,成为邮件正文中的内嵌图像。

这里是一个示例:

procedure SendMail();
var
  LHtmlPart: TIdText;
  LMessage: TIdMessage;
  LImagePart: TIdAttachmentFile;
  LHtmlText: String;
  LAttachment: TIdAttachmentFile;    
  SMTP: TIdSMTP;
  SSL: TIdSSLIOHandlerSocketOpenSSL;
begin    
  LMessage:= TIdMessage.Create(nil);
  try
    // Message stuff //
    LMessage.From.Text := 'email@mail.com';
    LMessage.From.Name:= 'from name';
    LMessage.Recipients.Add.Address := 'email@mail.com';
    LMessage.Subject := 'subject';
    LMessage.ContentType := 'multipart/mixed';

    // Build HTML message //
    LHtmlPart:= TIdText.Create(LMessage.MessageParts);
    LHtmlPart.ContentType:= 'text/html';
    LHtmlText:= TFile.ReadAllText('filename.html');

    // base64 to temporary file and attach images to message //
    DecodeHtmlImages(LHtmlText, LMessage, LImagePart);
    LHtmlPart.Body.Text:= LHtmlText;

    // Attachs (not inline images) //
    LAttachment:= TIdAttachmentFile.Create(LMessage.MessageParts, 'filename1');
    LAttachment.FileName:= ExtractFileName('filename1');

    LAttachment:= TIdAttachmentFile.Create(LMessage.MessageParts, 'filename2');
    LAttachment.FileName:= ExtractFileName('filename2');

    SSL:= TIdSSLIOHandlerSocketOpenSSL.Create(nil);
    SSL.SSLOptions.Mode:= sslmClient;
    SSL.SSLOptions.Method:= sslvSSLv23;

    SMTP:= TIdSMTP.Create(nil);
    SMTP.IOHandler:= SSL;
    SMTP.Host:= 'smtp.office365.com';
    SMTP.Port:=  587;
    SMTP.AuthType := satDefault;
    // ms mail service //
    SMTP.UseTLS:= utUseExplicitTLS;
    SMTP.Username:= 'email@email.com';
    SMTP.Password:=  'password';

    try
      SMTP.Connect();
      SMTP.Send(LMessage);
    except
      ShowMessage('Failed: ' + E.Message);
    end;

  finally
    LHtmlPart.Free;
    LImagePart.Free;
    LMessage.Free;
    SMTP.Free;
    SSL.Free;
  end;
end;

上面的代码是一个示例,它展示了如何将内嵌图像附加到邮件消息中。

另外,以下是上述代码中使用的一些附加函数:

procedure DecodeHtmlImages(var ABody: String; var AMessage: TIdMessage; var AImagePart: TIdAttachmentFile);
// ...
function ExtractBase64FromHTML(HTML: string): string;
// ...
function ExtractImageExtensionFromHTML(htmlContent: string): string;

这些函数用于处理HTML中的图像数据和文件扩展名。如果需要更多详细信息,请告诉我。

英文:

As I mentioned earlier my idea was (and is) similar of @Oleksandr Morozevych answer, I basically convert all images from body from base64 into a temporary binary (image) file which is attached on the mail message, and the body &lt;img src=&quot;data:image/png;base64,iVBORw0KGgoAAAAN... is replaced with &lt;img src=&quot;cid:cid_image_id.jpg&quot; /&gt;, becoming and inline image in the email body.

Here an example:

procedure SendMail();
var
  LHtmlPart: TIdText;
  LMessage: TIdMessage;
  LImagePart: TIdAttachmentFile;
  LHtmlText: String;
  LAttachment: TIdAttachmentFile;    
  SMTP: TIdSMTP;
  SSL: TIdSSLIOHandlerSocketOpenSSL;
begin    
  LMessage:= TIdMessage.Create(nil);
  try
    // Message stuff //
    LMessage.From.Text := &#39;email@mail.com&#39;;
    LMessage.From.Name:= &#39;from name&#39;;
    LMessage.Recipients.Add.Address := &#39;email@mail.com&#39;;
    LMessage.Subject := &#39;subject&#39;;
    LMessage.ContentType := &#39;multipart/mixed&#39;;

    // Build HTML message //
    LHtmlPart:= TIdText.Create(LMessage.MessageParts);
    LHtmlPart.ContentType:= &#39;text/html&#39;;
    LHtmlText:= TFile.ReadAllText(&#39;filename.html&#39;);

    // base64 to temporary file and attach images to message //
    DecodeHtmlImages(LHtmlText, LMessage, LImagePart);
    LHtmlPart.Body.Text:= LHtmlText;
    
    // Attachs (not inline images) //
    LAttachment:= TIdAttachmentFile.Create(LMessage.MessageParts, &#39;filename1&#39;);
    LAttachment.FileName:= ExtractFileName(&#39;filename1&#39;);

    LAttachment:= TIdAttachmentFile.Create(LMessage.MessageParts, &#39;filename2&#39;);
    LAttachment.FileName:= ExtractFileName(&#39;filename2&#39;);

    SSL:= TIdSSLIOHandlerSocketOpenSSL.Create(nil);
    SSL.SSLOptions.Mode:= sslmClient;
    SSL.SSLOptions.Method:= sslvSSLv23;

    SMTP:= TIdSMTP.Create(nil);
    SMTP.IOHandler:= SSL;
    SMTP.Host:= &#39;smtp.office365.com&#39;;
    SMTP.Port:=  587;
    SMTP.AuthType := satDefault;
    // ms mail service //
    SMTP.UseTLS:= utUseExplicitTLS;
    SMTP.Username:= &#39;email@email.com&#39;;
    SMTP.Password:=  &#39;password&#39;;

    try
      SMTP.Connect();
      SMTP.Send(LMessage);
    except
      ShowMessage(&#39;Failed: &#39; + E.Message);
    end;

  finally
    LHtmlPart.Free;
    LImagePart.Free;
    LMessage.Free;
    SMTP.Free;
    SSL.Free;
  end;

Additional functions I made used above:

procedure DecodeHtmlImages(var ABody: String; var AMessage: TIdMessage; var AImagePart: TIdAttachmentFile);
var
  LStream: TMemoryStream;
  LMatch: TMatch;
  LMatches: TMatchCollection;
  LBase64: String;
  LImageData: TBytes;
  LFilename: String;
const
  EXP_ENCODED_SOURCE = &#39;src\s*=\s*&quot;([cid^].+?)&quot;&#39;;
begin

  LMatches:= TRegEx.Matches(ABody, EXP_ENCODED_SOURCE, [roIgnoreCase]);
  for LMatch in LMatches do
  begin
    // step 1 - convert and save temp file //
    LBase64:= ExtractBase64FromHTML(LMatch.Value);
    Lstream := TBytesStream.Create(TNetEncoding.Base64.DecodeStringToBytes(LBase64));
    try
      LFilename:= IncludeTrailingPathDelimiter(System.IOUtils.TPath.GetTempPath) + &#39;tmp_&#39; + FormatDateTime(&#39;yyyymmddhhnnsszzz&#39;, Now) + &#39;.&#39; + ExtractImageExtensionFromHTML(ABody);
      LStream.SaveToFile(LFilename);
    finally
      LStream.Free;
    end;

    // step 2 - replace base64 code for &quot;cid&quot; and attach all images //
    if FileExists(LFilename) then
    begin
      AImagePart:= TIdAttachmentFile.Create(AMessage.MessageParts, LFilename);
      try
        AImagePart.ContentType:= Format(&#39;image/%s&#39;, [StringReplace(TPath.GetExtension(LFilename), &#39;.&#39;, &#39;&#39;, [rfIgnoreCase])]);
        AImagePart.ContentDisposition:= &#39;inline&#39;;
        AImagePart.FileIsTempFile:= True;
        AImagePart.ExtraHeaders.Values[&#39;content-id&#39;]:= TPath.GetFileName(LFilename);
        AImagePart.DisplayName:= TPath.GetFileName(LFilename);

        ABody:= StringReplace(ABody, LMatch.Value, Format(&#39;src=&quot;cid:%s&quot;&#39;, [TPath.GetFileName(LFilename)]), [rfIgnoreCase]);
      finally
        //freeAndNil(LImagePart);      // cant be freed yet //
      end;
    end;
  end;
end;

function ExtractBase64FromHTML(HTML: string): string;
var
  RegEx: TRegEx;
  Match: TMatch;
begin
  RegEx := TRegEx.Create(&#39;data:image\/[a-zA-Z]*;base64,([^&quot;]+)&#39;, [roIgnoreCase]);
  Match := RegEx.Match(HTML);

  if Match.Success then
    Result := Match.Groups[1].Value
  else
    Result := &#39;&#39;;
end;

function ExtractImageExtensionFromHTML(htmlContent: string): string;
var
  regex: TRegEx;
  match: TMatch;
begin
  regex := TRegEx.Create(&#39;data:image\/(.*?);base64&#39;);
  match := regex.Match(htmlContent);
  if match.Success then
    Result := match.Groups.Item[1].Value
  else
    Result := &#39;&#39;;
end;

I made this for test and works well, it is able to send multiple images that is in the html message (need be in base64 format), in a near future I'll refactor entire code to interfaced interact to segregate and decouple the code.

huangapple
  • 本文由 发表于 2023年6月2日 03:22:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76385074.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定