SmtpClient/MailMessage 上的主题编码

Posted

技术标签:

【中文标题】SmtpClient/MailMessage 上的主题编码【英文标题】:Subject encoding on SmtpClient/MailMessage 【发布时间】:2013-04-08 08:51:09 【问题描述】:

我正在尝试使用 SmtpClientMailMessage 类发送包含非 ASCII 字符的电子邮件。

我正在使用外部邮件服务 (MailChimp),我的一些电子邮件已被他们的 SMTP 服务器拒绝。我已经联系了他们,他们是这样回复的:

似乎主题行是 Base64 编码,然后是 Quoted-Printable 编码,这通常应该没问题,但其中一个字符被分成两行。因此,当您的主题行有点长时,为了正确处理,它被分成两行。在主题行中使用 UTF-8 引用可打印时,不应在行之间断开字符串。相反,应该缩短一行,以便完整的字符串保持在一起。在这种情况下,这不会发生,因此表示单个字符的字符串被分成多行,因此不是有效的 UTF-8 可引用打印编码。

有问题的主题如下:

Subject: XXXXXXX - 5 personnes vous ont nommé guide

在 UTF-8/Base64 中是这样的:

Subject: WFhYWFhYWCAtIDUgcGVyc29ubmVzIHZvdXMgb250IG5vbW3DqSBndWlkZQ==

由于该标头会超过某个最大长度(我不确定是 Quoted-Printable 编码及其每行 76 个字符的限制,还是 SMTP 标头限制),在编码和拆分后,标头将变为:

Subject: =?utf-8?B?WFhYWFhYWCAtIDUgcGVyc29ubmVzIHZvdXMgb250IG5vbW3D?=
 =?utf-8?B?qSBndWlkZQ==?=

显然这在解码时会导致问题(因为第一行无法解码为有效字符串)。我不确定我是否完全理解了这个问题,我有以下问题:

为什么是 ?utf-8?B?部分重复? QP编码不应该在分割线之前发生,因此它的标题不应该重复吗? QP 解码后,我们不应该得到一个有效的 1 行 Base64 字符串吗? 第二行开头有一个空格,超出了 QP 编码,这可能是问题吗? 是编码器坏了,还是解码器坏了?

另请注意,其他一些 SMTP 服务器会接受此消息,但这并不意味着它是有效的。

作为一种解决方法,我尝试禁用 Base64 编码,这显然是不必要的,但是 MailMessage 类有一个 BodyTransferEncoding 属性来控制这种编码,但仅适用于邮件的正文部分。似乎没有属性可以控制主题的“传输”编码。

【问题讨论】:

这里有同样的问题。当邮件主题标头使用 Base64 或 QuotedPrintable (EncodedWord RFC2047) 编码时,我可以重现它 【参考方案1】:

MSDN 论坛已确认这是一个错误:http://social.msdn.microsoft.com/Forums/vstudio/en-US/4d1c1752-70ba-420a-9510-8fb4aa6da046/subject-encoding-on-smtpclientmailmessage

并且在 Microsoft Connect 上提交了一个错误: https://connect.microsoft.com/VisualStudio/feedback/details/785710/mailmessage-subject-incorrectly-encoded-in-utf-8-base64

一种解决方法是将 MailMessage 的 SubjectEncoding 设置为其他编码,例如 ISO-8859-1。在这种情况下,主题将被编码为 Quoted Printable(不是 Base64),从而避免了该问题。

【讨论】:

解决方法对我不起作用。我的非 ascii 字符仍然在主题中显示为“看起来很奇怪的符号”。 :( 您可能有不同的问题。我的不是奇怪的字符,而是一些 SMTP 服务器拒绝了电子邮件。【参考方案2】:

更好的解决方案是使用Encoding.Unicode 而不是Encoding.UTF8 来代替SubjectEncoding

看来,由于 Microsoft 实施只是忽略了 UTF-16 能够将字符编码为两个以上字节的现实(如 Why does C# use UTF-16 for strings? 所见),因此稳定的字符大小会有所帮助。

我在https://gist.github.com/dbykadorov/9047455 上看到过这个。

【讨论】:

【参考方案3】:

我对这个问题的解决方案是某种技巧!

我在邮件主题中使用波斯语,并在 .Net 框架 4.5.2 中使用 SmtpClient 发送邮件。 收到的消息主题在某些位置显示一些垃圾词,例如主题字符串中的第 18 和第 38 个字符。无论主题是什么。

然后我尝试在这些位置插入一些空格(字符 32),重新发送邮件后结果非常好。 unicode 主题按预期显示。

所以我写了一个函数来在我需要的位置插入 6 个空格(避免在单词中插入空格),如下所示:

private static string InsertSpacesBetweenWords(this string subject , int where)
    
        int l;
        int i=1;
        string[] s = subject.Split(new string[]  " " ,  StringSplitOptions.RemoveEmptyEntries);
        string output = "";

        if (s.Length > 0) output += s[0] + " ";
        l = output.Length;
        bool done = false;

        while (i < s.Length)
        
            if (!done)
            
                if ((s[i] + output).Length > where)
                
                    for (int j = output.Length; j < where + 6; j++)
                        output += " ";
                    done = true;
                
            
            output += s[i] + " ";
            i++;
        
        return output;
    

然后我使用此功能转换了邮件主题:

mail.Subject = mySubject.InsertSpacesBetweenWords(38).InsertSpacesBetweenWords(18);

有趣的是,Gmail 和 Yahoo 邮件(可能还有其他基于 Web 的邮件系统)会忽略多余的空格并按预期显示主题。

【讨论】:

【参考方案4】:

为主题添加两个空格对我有用。不要问为什么。

var mail = new MailMessage(from, to);
mail.Subject = subject + new string(new char[]  '\u2000', '\u2000' );
mail.SubjectEncoding = Encoding.UTF8;

【讨论】:

以上是关于SmtpClient/MailMessage 上的主题编码的主要内容,如果未能解决你的问题,请参考以下文章

同一应用程序上的两种不同语言 PHP 和 Java,Apache 上的 PHP 和 Tomcat 上的 Java

UILabel 上的奇怪行为与 StoryBoard 上的 \n

iPhone 上的字体大小比 PC 上的小

怎么把图片上的字去掉啊?

在 Firefox 中不显示边框,表格上的边框折叠,位置:tbody 上的相对或单元格上的背景颜色

键盘上的PERIOD键在啥地方?