为什么文本文件的编码从 UTF-8 更改为 ANSI(以及如何解决这个问题)?

Why has the textfile's encoding changed from UTF-8 to ANSI (and how to solve this)?

我正在使用 Indy 从 Android 设备发送一个 .txt 文件作为附件。

当我下载此文件时,.txt 文件的编码已从 UTF-8 更改为 ANSI,并在一行中显示其内容,而不是在一行下方。

那么我做错了什么,如何解决这个问题?

用于发送邮件的功能代码:

Body := TStringList.Create;
IdSMTP := TIdSMTP.Create(nil);
SSLHandler:= TIdSSLIOHandlerSocketOpenSSL.Create(Form1);
IdSMTP.IOHandler:= SSLHandler;
IdSMTP.UseTLS:= utUseExplicitTLS;

IdSMTP.Host := 'smtp.gmail.com';
IdSMTP.Port := 587;
IdSMTP.AuthType := satDefault;
IdSMTP.Username := AppData.MailAddrSender;
IdSMTP.Password := Appdata.MailPassword;

IdMessage := TIdMessage.Create(nil);
IdMessage.From.Name := aName;
IdMessage.From.Address := AppData.MailAddrSender;
IdMessage.Subject := 'E-mail subject';

PathString := System.IOUtils.TPath.Combine(System.IOUtils.TPath.GetDocumentsPath, (PathString + 'Log.txt'));
Body.Add('Mail todays log,');
if FileExists(PathString) then
  TIdAttachmentFile.Create(IdMessage.MessageParts,PathString);
try
  for I := 0 to Body.Count -1 do
    IdMessage.Body.Add(Body.Strings[I]);
  IdEmailAddressItem := IdMessage.Recipients.Add;
  IdEmailAddressItem.Address := AppData.MailAddrReceiver;

  try
    IdSMTP.Connect;
    try
      if IdSMTP.Authenticate then
      IdSMTP.Send(IdMessage);

    finally
      IdMessage.Free;
      IdSMTP.Disconnect;
    end;

  except
    on E: Exception do
    //exception handling here
  end;
finally
  IdSMTP.Free;
  SSLHandler.DisposeOf;
  Body.DisposeOf;
end;

感谢您的宝贵时间。

When I download this, the .txt file's encoding has changed from UTF-8 to ANSI

使用TIdAttachmentFile时,文件的原始字节按原样发送。 Indy 不会更改附件的编码。

TIdAttachmentFile 根据您传递给构造函数的文件名的扩展名默认其 ContentType 属性 值。在本例中,扩展名是 .txt,因此 ContentType 应该默认为 text/plaintext/... 媒体类型有一个与之关联的 charset 属性,但您没有在代码中设置附件的 Charset 属性。否则,收件人将使用默认值 charset 来解释数据(根据 RFC 822 通常为 us-ascii)。因此,收件人可能正在检测缺少 charsettext/plain 媒体类型,并且正在 显示 ASCII/ANSI 中的文件数据,因为它确实不知道数据实际上是 UTF-8 编码的(因为文件是附件,如果将附件保存到新文件,收件人应该 保存 原始字节)。

如果您知道附加的文件是以 UTF-8 编码的事实,您应该明确说明:

if FileExists(PathString) then
begin
  Attachment := TIdAttachmentFile.Create(IdMessage.MessageParts, PathString);
  Attachment.ContentType := 'text/plain';
  Attachment.Charset := 'utf-8';
end;

and is displaying it's content in lines next to eachother instead of underneath eachother.

这听起来更像是换行符问题,而不是编码问题。例如,如果 Android 上的原始文件使用 LF 换行符,但收件人只支持 CRLF 换行符。

如果需要,您可以让 Indy 将换行符规范化为 CRLF。您只需将文件数据加载到 TIdText 而不是 TIdAttachmentFile,然后确保将 TIdText.ContentDisposition 属性 设置为 attachment(否则它将默认为 inline),并设置 TIdText.FileName,因此数据仍被收件人视为附件:

if FileExists(PathString) then
begin
  Attachment := TIdText.Create(IdMessage.MessageParts, nil);
  Attachment.Body.LoadFromFile(PathString, TEncoding.UTF8);
  Attachment.ContentType := 'text/plain';
  Attachment.Charset := 'utf-8';
  Attachment.ContentDisposition := 'attachment';
  Attachment.FileName := ExtractFileName(PathString);
end;

现在,综上所述,您在使用 Indy 的组件时通常还存在一些其他小的编码问题。我会建议更像这样的东西:

try
  IdSMTP := TIdSMTP.Create(nil);
  try
    SSLHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdSMTP);
    IdSMTP.IOHandler := SSLHandler;
    IdSMTP.UseTLS := utUseExplicitTLS;

    IdSMTP.Host := 'smtp.gmail.com';
    IdSMTP.Port := 587;
    IdSMTP.AuthType := satDefault;
    IdSMTP.Username := AppData.MailAddrSender;
    IdSMTP.Password := Appdata.MailPassword;

    IdMessage := TIdMessage.Create(nil);
    try
      IdMessage.From.Name := aName;
      IdMessage.From.Address := AppData.MailAddrSender;
      IdMessage.Subject := 'E-mail subject';

      IdEmailAddressItem := IdMessage.Recipients.Add;
      IdEmailAddressItem.Address := AppData.MailAddrReceiver;

      IdMessage.Body.Add('Mail todays log,');

      PathString := System.IOUtils.TPath.Combine(System.IOUtils.TPath.GetDocumentsPath, PathString + 'Log.txt');
      if FileExists(PathString) then
      begin
        // or TIdText...
        Attachment := TIdAttachmentFile.Create(IdMessage.MessageParts, PathString);
        Attachment.ContentType := 'text/plain';
        Attachment.Charset := 'utf-8';
      end;

      IdSMTP.Connect;
      try
        IdSMTP.Send(IdMessage);
      finally
        IdSMTP.Disconnect;
      end;
    finally
      IdMessage.Free;
    end;
  finally
    IdSMTP.Free;
  end;
except
  on E: Exception do
    //exception handling here
end;