TIdAttachment - 获取正确的附件文件名 - 没有 utf-8 编码信息

Posted

技术标签:

【中文标题】TIdAttachment - 获取正确的附件文件名 - 没有 utf-8 编码信息【英文标题】:TIdAttachment - Getting the correct filename of Attachment - without the utf-8 encoding information 【发布时间】:2020-07-28 06:03:27 【问题描述】:

在我的项目中,我尝试从 .eml 文件中提取附件。

在正常情况下(字符集 ISO),它可以工作。但是在特殊情况下,它给了我错误的附件文件名。

这是我示例中的 MessagePart 的样子:

------=_Part_315_1401515384.1585891801067
Content-Type: application/octet-stream; 
    name="=?UTF-8?Q?Report=5F2020-3=5FCustomerA with some spaces in between.csv?="
Content-Transfer-Encoding: base64
Content-Disposition: attachment; 
    filename="=?UTF-8?Q?Report=5F2020-3=5FCustomerA with some spaces in between.csv?="

UGFydG5lcjtNYW5kYW50ZW5uYW1lO05hbWU7RmlybWE7U3RyYd9lO1Bvc3RsZWl0emFobDtPcnQ7
TGFuZDtQcm9kdWt0bGluaWU7S3VuZGVuc3RhdHVzO0RhdHVtIFJlZ2lzdHJpZXJ1bmc7QmVnaW5u
IEthdWY7S/xuZGlndW5nIGf8bHRpZyBhYjtBbnphaGwgQmVudXR6ZXI7QW56YWhsIE1vYmlsZSBz
eW5jO05ldHRvIFJlY2hudW5nc2JldHJhZyBpbiBFdXJvO1Byb3Zpc2lvbnNzdHVmZTtQcm92aXNp
b25zYW50ZWlsIGluIEV1cm8NCg==
------=_Part_315_1401515384.1585891801067--

到目前为止没有什么了不起的。但由于某种原因,我没有从该附件中获得正确的文件名。

这是我获取该文件的文件名并将其保存到临时位置的代码:

function foo(MyMail: TIdMessage; SavePathWithoutBackSlash : string): boolean;
var
 i : Integer;
 lfilename: string;
begin
  for i := 0 to Pred(MyMail.MessageParts.Count) do
    begin
      if (MyMail.MessageParts.Items[i] is TIdAttachmentFile) then
      begin
        lFilename := TIdAttachmentFile(MyMail.MessageParts.Items[i]).FileName;
        TIdAttachmentFile(MyMail.MessageParts.Items[i]).SaveToFile(SavePathWithoutBackSlash + '\' + lFilename);
      end;
    end;
  end; 
end;

我已经尝试了很多编码,但似乎没有任何影响。

我期望的是这样的字符串:path/Report_2020-3_CustomerA.csv

我得到的是:path/=?UTF-8?Q?Report=5F2020-3=5FCustomerA.csv?=

如何正确保存附件?

【问题讨论】:

综合答案很复杂,解决方案要求您递归遍历整个电子邮件层次结构,检查每个节点的MyMail.ContentType,以确定您是在“节点”上还是在附件上.附言您的示例代码使用 [ii],但您的循环使用 i。 你试过DecodeHeader()吗? @nolanspeaker - 你的意思是什么?检查每个 Messagepart 是否为 TIdAttachmentFile。如果它会被Namen保存,我不明白你的意思。顺便提一句。我更正了 ii,感谢提供信息。 @Olivier - 我应该如何使用 DecodeHeader?我尝试了 DecodeHeader(TidAttachmentFile.FileName) 但结果是一样的。 @olivier - 在这种情况下,我使用 Base64 测试一个可以工作的字符串(=?UTF-8?B?) DecodeHeader 做的一切都是正确的,但在我的示例中它是引用/可打印的(=?UTF -8?Q?) 对此有何建议? 【参考方案1】:

感谢@olivier,我找到了解决该问题的方法。它是 2 个问题的组合。

第一个问题:

TIdAttachmentFile(MyMail.MessageParts.Items[i]).FileName

不返回=?UTF-8?Q? 它返回=_UTF-8_Q_ 所以我把它改成了

TIdAttachmentFile(MyMail.MessageParts.Items[i]).Name

-

第二个问题:

根据 RFC2047,=?...?= 之间不允许有空格。所以 DecodeHeader 将保持字符串不变。我的代码现在可以工作了,看起来像这样:

Uses 
  ...IdAttachmentFile, IdMessage, IdCoderHeader...  

function foo(MyMail: TIdMessage; SavePathWithoutBackSlash : string): boolean;
    var
     i : Integer;
     lfilename: string;
    begin
      for i := 0 to Pred(MyMail.MessageParts.Count) do
        begin
          if (MyMail.MessageParts.Items[i] is TIdAttachmentFile) then
          begin
            lFilename := TIdAttachmentFile(MyMail.MessageParts.Items[i]).Name;
            if pos('=?UTF-8?Q?',uppercase(lfilename)) > 0 then
            begin
              lfilename:=StringReplace(lFilename,' ','=20',[rfReplaceAll,rfIgnoreCase]);
            end;
            lFileName:= DecodeHeader(lFileName);
            TIdAttachmentFile(MyMail.MessageParts.Items[i]).SaveToFile(SavePathWithoutBackSlash + '\' + lFilename);
          end;
        end;
      end; 
    end;

=20 是 UTF-8 中带引号打印的空格的正确表示法

【讨论】:

空格是有效的 UTF-8 字符串。只是 RFC 2047 禁止 =?...?= 构造中的文字空格。 您在问题中显示的数据中没有未编码的空格。而且 Indy 不会像您声称的那样将 =?UTF-8?Q? 转换为 =_UTF-8_Q_。你绝对应该使用FileName 而不是Name。所以我看不出这两种解决方案有什么不同。 @Remy,是的,您在我的示例中是对的,没有未编码的空格,我更改了数据以防止信息在 Internet 上生效。因为我没有意识到这个问题,所以我只留下空白。 FileName 和 Name 和我写的完全一样。如果我使用 Filename 函数,Indy 将不会给我任何问号。每个问号都替换为 _ @fisi-pjm 您所描述的根本不是 Indy 的工作方式。如果 Indy 无法解码 NameFileName,那么它将保留原始数据。因此,您获得=_UTF-8_Q_唯一 方式是,如果这是开始下载的内容。 @Remy,感谢您的评论。我将在下周再次检查。我有一个系统,它使用一个组件将邮件导出到 eml 文件。我只检查了该系统内的原始标题,而不是导出后得到的。导出工具可能会更改数据。谢谢你的提示。

以上是关于TIdAttachment - 获取正确的附件文件名 - 没有 utf-8 编码信息的主要内容,如果未能解决你的问题,请参考以下文章

无需下载即可获取 Gmail 附件文件名

从多部分/替代消息中获取附件作为字符串[重复]

如何获取电子邮件所有附件的文件名?

有没有办法从 PST 文件中获取附件名称?

javamail: UrlDataSource获取网络文件作为邮件的附件|javamail发送二进制流附件的问题

打开电子邮件附件时如何获取文件名和扩展名