“?”在邮件头中删除附件文件名 (UTF-8) Exchange 服务器

问题描述

我们使用 JavaMail 发送带有 PDF 附件的邮件。当文件名中存在 Unicode 字符时,附件似乎被命名为 UTF 编码名称。进一步检查邮件头后发现文件名 MIME 中的 ?删除。例如

预期:

Content-disposition: attachment; 
    filename="=?utf8?Q?hinzugef=C3=BCgte.pdf?="

获得:

Content-disposition: attachment; 
    filename="=utf8Qhinzugef=C3=BCgte.pdf="

因此,附件中的文件名是 =utf8Qhinzugef=C3=BCgte.pdf=,我们无法打开它。

如果我手动修改 .eml 文件并在正确的位置添加 ? 并在 Outlook 中打开它,该文件会按预期以 PDF 格式显示

此问题已在 Exchange 服务器中报告,我们无法在 Gmail 或 Fake SMTP(在我的机器上,用于测试邮件)中重现它

示例代码

MimeBodyPart mbp2 = new MimeBodyPart();
String attFileName = file.getName();
String i18nFileName = new String(attFileName.getBytes(),"UTF-8");
String mimeType = mimeMap.getContentType(attFileName);
attStream = new FileInputStream(att);
ByteArrayDataSource bas = new ByteArrayDataSource(attStream,mimeType);
mbp2.setDataHandler(new DataHandler(bas));
mbp2.setFileName(MimeUtility.encodeText(i18nFileName));
mp.addBodyPart(mbp2);
if (attStream != null) {
    attStream.close();
}

为什么会这样?任何线索都会非常有帮助

解决方法

这是错误的编码开始。

  • 您实施的是 RFC 2047,但这根本不适用于 HTTP。
  • RFC 6266 § 4.3 解释了如何处理该 HTTP 标头的 filename= 参数,然后参考
  • RFC 5897,已被关于如何合并非 ASCII 的 RFC 8187 § 3.2.3 废弃。

通用形式是 filename*=UTF-8''Na%C3%AFve%20file.txt,它与您实施的 RFC 2047 在几个方面有所不同:

    应该使用
  • filename*= - 注意参数后面的 星号。这是为了表示扩展符号 - 否则字符集和百分比编码都不会出现。
  • 使用扩展符号时,不需要也不允许将值括在 "引号" 中。
  • 同样,前缀 =?、后缀 ?=?Q? 编码参数都是预期的。从逻辑上讲,它们也没有任何意义,因为只有 引用编码 可用,并且整个非 ASCII 范围是完整的,而不仅仅是某个地方。
  • '' 部分用于可选的语言代码 - 对于英语可能是 'en',但实际上没有人关心这一点。
  • 剩下的就很简单了:UTF-8 字符序列的每个字节都被引用编码。 空格也必须用引号编码(说:%20)。
  • 正确的字符集是 UTF-8,而 utf8 是错误的 - 不要依赖于被那个非官方别名所接受,尽管它偶尔会被容忍。

换句话说:客户的行为是正确的。如果我使用 Thunderbird 68 并按 CTRL+Q 查看电子邮件的来源,或者将电子邮件另存为 .EML 文件然后查看该文件,我有一个 {{3 }} 其中每个附件都有标题

Content-Disposition: inline;
    filename*=utf-8''L%20%2D%20qualita%CC%88t.pdf
Content-Type: application/pdf;
    x-unix-mode=0644;
    name="=?utf-8?Q?L_-_qualita=CC=88t=2Epdf?="

不要混淆,因为您现在看到了这两种变体 - 它们仍然具有不同的目的和不同的上下文。您需要的主要是文件名(尽管提供名称也无妨)。如果您仔细观察,这些值也不同(前者有空格,后者使用下划线 - 但这是发件人的自由决定)。 UTF-8 字符序列 %CC%88=CC=88 是代码点 U+0308 = ̈ COMBINING DIAERESIS(将 a 放在 ä 之前)。>

multipart 解释了 2011 年 HTTP 浏览器对待 RFC 5897 的不同。