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 无法解码 Name
和 FileName
,那么它将保留原始数据。因此,您获得=_UTF-8_Q_
的唯一 方式是,如果这是开始下载的内容。
@Remy,感谢您的评论。我将在下周再次检查。我有一个系统,它使用一个组件将邮件导出到 eml 文件。我只检查了该系统内的原始标题,而不是导出后得到的。导出工具可能会更改数据。谢谢你的提示。以上是关于TIdAttachment - 获取正确的附件文件名 - 没有 utf-8 编码信息的主要内容,如果未能解决你的问题,请参考以下文章