无法使用 TIdSMTP 发送 UTF-8 消息

Cannot send UTF-8 message with TIdSMTP

我有发送邮件的按钮:

procedure TRealization.Button17Click(Sender: TObject);
var
  EmailCore: TStringStream;
  EmailBody, sInvoices: string;
  FilePath: string;
  MailTitle: string;
  i: integer;
begin
  if (Languages.ItemIndex > -1) then begin
    EmailCore := TStringStream.Create('', TEncoding.UTF8);
    if Languages.ItemIndex = 0 then begin
        FilePath := ExtractFilePath(Application.ExeName)+'/languages/pl.html';
        MailTitle := 'Płatność dla '+Monitoring.l_client_name.Caption;
    end else if Languages.ItemIndex = 1 then begin
        FilePath := ExtractFilePath(Application.ExeName)+'/languages/en.html';
        MailTitle := 'Invoice payment for '+Monitoring.l_client_name.Caption;
    end;
    EmailCore.LoadFromFile(FilePath);
    for i := 0 to (preview_invoices.RowCount-1) do begin
      sInvoices := sInvoices+'<tr><td>'+preview_invoices.Cells[2,i]+'</td><td>'+preview_invoices.Cells[4,i]+'</td><td>'+preview_invoices.Cells[4,i]+'</td><td>'+preview_invoices.Cells[5,i]+'</td><td>'+preview_invoices.Cells[6,i]+'</td></tr>';
    end;
    EmailBody := StringReplace(EmailCore.DataString, ':LIST:', sInvoices, [rfReplaceAll]);
    EmailBody := StringReplace(EmailBody, ':COMPANY:', Monitoring.l_client_name.Caption, [rfReplaceAll]);
    EmailBody := StringReplace(EmailBody, ':VAT:', Monitoring.l_client_vat.Caption, [rfReplaceAll]);
    EmailBody := StringReplace(EmailBody, ':NAME:', Main.l_uname.Caption, [rfReplaceAll]);
    Main.SendEmailIndy('my.mail.com',
        Main.l_uname.Caption,
        _GlobalData.EMail,
        Emails.Text,
        _GlobalData.EMail,
        '',
        MailTitle,
        EMailBody,
        true,
        nil);
    EmailCore.Free;
  end;
end;

这是发送函数 e-mails:

procedure TMain.SendEmailIndy(
        const SMTPServer: string;
        const FromName, FromAddress: string;
        const ToAddresses: string; //comma "," separated list of e-mail addresses
        const CCAddresses: string; //comma "," separated list of e-mail addresses
        const BCCAddresses: string; //comma "," separated list of e-mail addresses
        const Subject: string;
        const EmailBody: string;
        const IsBodyHtml: Boolean; //verses Plain Text
        const Attachments: TStrings);
var
    smtp: TIdSMTP; // IdSmtp.pas
    msg: TidMessage; // IdMessage.pas
    builder: TIdCustomMessageBuilder; //IdMessageBuilder.pas
    s: string;
    emailAddress: string;
begin
    msg := TidMessage.Create(nil);
    msg.Encoding := meMIME;
    msg.ContentType := 'text/html';
    msg.CharSet := 'UTF-8';
    msg.ContentTransferEncoding:= 'quoted-printable';
    try
        if IsBodyHtml then begin
            builder := TIdMessageBuilderHtml.Create;
            TIdMessageBuilderHtml(builder).Html.Text := EmailBody
        end else begin
            builder := TIdMessageBuilderPlain.Create;
        end;

        try
            if Attachments <> nil then
            begin
                for s in Attachments do
                    builder.Attachments.Add(s);
            end;

            builder.FillMessage(msg);
        finally
            builder.Free;
        end;

        msg.From.Name := FromName;
        msg.From.Address := FromAddress;
        msg.Subject := Subject;

        //If the message is plaintext then we must fill the body outside of the PlainText email builder.
        //(the PlainTextBuilder is unable to build plaintext e-mail)
        if not IsBodyHtml then begin
            msg.Body.Text := EmailBody;
        end;

        for s in ToAddresses.Split([',']) do
        begin
            emailAddress := Trim(s);
            if emailAddress <> '' then
            begin
                with msg.recipients.Add do
                begin
                    //Name := '<Name of recipient>';
                    Address := emailAddress;
                end;
            end;
        end;

        for s in CCAddresses.Split([',']) do
        begin
            emailAddress := Trim(s);
            if emailAddress <> '' then
                msg.CCList.Add.Address := emailAddress;
        end;

        for s in BCCAddresses.Split([',']) do
        begin
            emailAddress := Trim(s);
            if emailAddress <> '' then
                msg.BccList.Add.Address := emailAddress;
        end;

        smtp := TIdSMTP.Create(nil);
        try
            smtp.Host := SMTPServer; // IP Address of SMTP server
            Smtp.UseTLS := utNoTLSSupport;
            smtp.Port := 587; //The default already is port 25 (the SMTP port)
            smtp.Username := _GlobalData.EMail;
            smtp.Password := _GlobalData.Password;
            //Indy (and C# SmtpClient class) already defaults to the computer name
            //smtp.HeloName :=
            smtp.Connect;
        try
            smtp.Send(msg);
            ShowMessage('Wiadomość wysłana.');
            log('EMAIL', 'Wysłano zapytanie do '+ToAddresses, StrToInt(Monitoring.t_mid.Caption));
        finally
          //smtp.Disconnect();
        end;
    finally
        //smtp.Free;
    end;
finally
    //msg.Free;
end;
end;

问题是,当我收到一封电子邮件时,body(e-mail 标题实际上没问题,可以显示所有字符)包含问号,其中波兰语或德语字母是(±、ż、 ó, ę, ö, ä 等)。邮件编码错误。我尝试使用 UTF8Encode 和此处找到的其他解决方案,但实际上没有任何效果。 E-mails 继续发送损坏的内容。

如有任何帮助,我将不胜感激。

您对 TIdMessage.CharSet 的分配被忽略,因为 TIdMessageBuilder 重置了 CharSet(以及 ContentTypeContentTransferEncodingEncoding 属性) 在填充 TIdMessage.

之前

因此,您需要改为设置 TIdMessageBuilderPlain.PlainTextCharSetTIdMessageBuilderHtml.HtmlCharSet 属性。

此外,您不需要单独使用 TIdMessageBuilderPlainTIdMessageBuilderHtml。您可以单独使用 TIdMessageBuilderHtml,因为它可以创建纯文本和 HTML 电子邮件,具体取决于填充的属性。您的评论“如果邮件是明文,那么我们必须在 PlainText 电子邮件生成器之外填充正文。PlainTextBuilder 无法构建明文电子邮件”是完全不正确的,如中所述my blog article on how to use TIdMessageBuilderHtml.

试试这个功能代码:

procedure TMain.SendEmailIndy(
  const SMTPServer: string;
  const FromName, FromAddress: string;
  const ToAddresses: string; //comma "," separated list of e-mail addresses
  const CCAddresses: string; //comma "," separated list of e-mail addresses
  const BCCAddresses: string; //comma "," separated list of e-mail addresses
  const Subject: string;
  const EmailBody: string;
  const IsBodyHtml: Boolean; //verses Plain Text
  const Attachments: TStrings);
var
  smtp: TIdSMTP; // IdSmtp.pas
  msg: TidMessage; // IdMessage.pas
  builder: TIdMessageBuilderHtml; //IdMessageBuilder.pas
  s: string;
  emailAddress: string;
begin
  msg := TIdMessage.Create(nil);
  try
    builder := TIdMessageBuilderHtml.Create;
    try
      if IsBodyHtml then
      begin
        builder.Html.Text := EmailBody;
        builder.HtmlCharSet := 'utf-8';
      end else
      begin
        builder.PlainText.Text := EmailBody;
        builder.PlainTextCharSet := 'utf-8';
      end;

      if Attachments <> nil then
      begin
        for s in Attachments do
          builder.Attachments.Add(s);
      end;

      builder.FillMessage(msg);
    finally
      builder.Free;
    end;

    msg.From.Name := FromName;
    msg.From.Address := FromAddress;
    msg.Subject := Subject;

    msg.Recipients.EmailAddresses := ToAddresses;
    msg.CCList.EmailAddresses := CCAddresses;
    msg.BccList.EmailAddresses := BCCAddresses;

    smtp := TIdSMTP.Create(nil);
    try
      smtp.Host := SMTPServer; // IP Address of SMTP server
      smtp.UseTLS := utNoTLSSupport;
      smtp.Port := 587; //The default already is port 25 (the SMTP port)
      smtp.Username := _GlobalData.EMail;
      smtp.Password := _GlobalData.Password;
      //Indy (and C# SmtpClient class) already defaults to the computer name
      //smtp.HeloName :=
      smtp.Connect;
      try
        smtp.Send(msg);
        log('EMAIL', 'Wysłano zapytanie do '+ToAddresses, StrToInt(Monitoring.t_mid.Caption));
      finally
        smtp.Disconnect;
      end;
      ShowMessage('Wiadomość wysłana.');
    finally
      smtp.Free;
    end;
  finally
    msg.Free;
  end;
end;

此外,仅供参考,如果您使用 UseTLS=utNoTLSSupport,那么您应该使用 Port=25。如果你想使用 Port=587 那么你应该使用 UseTLS=utUseExplicitTLS (这需要将 SSLIOHandler 分配给 TIdSMTP.IOHandler 属性,例如 TIdSSLIOHandlerSocketOpenSSL).