Django EmailMultiAlternatives 和 Win2003 上 Outlook 2003 的 HTML 邮件显示

2 投票
1 回答
5584 浏览
提问于 2025-04-15 12:04

我在用django.core.mail.EmailMultiAlternatives发送邮件,这样可以确保如果邮件客户端不支持HTML格式,消息会自动降级为文本格式。

这是我的send_email方法:

def send_email(self, from_address, to_list, subject, msg_text, msg_html):
        subject=subject.replace('\r','').replace('\n',' ')
        self.msg = EmailMultiAlternatives(subject, msg_text, from_address, to_list)
        self.msg.attach_alternative(msg_html, "text/html")
        self.msg.content_subtype = "html"
        self.msg.send()

在Gmail、Hotmail和很多其他邮件客户端上效果很好,HTML内容显示得没问题。但是在运行Windows 2003的Outlook 2003上,它只显示文本版本,不显示HTML内容。

如果我强制在EmailMultiAlternatives调用中放入HTML内容,也就是用msg_html代替msg_text,像这样:

self.msg = EmailMultiAlternatives(subject, msg_html, from_address, to_list)

那么在所有客户端上都能正常工作;但这样就意味着对于那些不支持HTML的客户端,或者更可能是那些禁用了HTML支持的客户端,就没有文本备选方案了。

我觉得值得一提的是,这封邮件是在运行Mac OS X的django应用上生成的(以防这和不同操作系统之间的换行符有关系)。

我看到其他语言的用户也遇到过类似的问题,尤其是在使用Outlook时……

我想知道有没有人知道为什么Outlook会表现得不同,以及是否有简单的解决办法可以在我的代码中应用?

1 个回答

5

我没有安装Outlook来测试这个,所以我想知道你函数里的第五行是干嘛的。

self.msg.content_subtype = "html"

我对多部分邮件的内部结构了解不多,但在我的系统上,那一行代码让消息的两个部分都变成了内容类型为text/html。如果不加这一行,第一部分的内容类型会是"text/plain",第二部分则是"text/html"。

无论如何,有一个关于Java的回答提到要把字符集改成iso-8859-1。我觉得你可以用django.core.mail来做到这一点。

EmailMessage类(EmailMultiAlternatives是从这个类继承的)有一个叫“encoding”的属性,可以设置要使用的字符集。默认情况下,这个属性是None,所以会使用utf-8作为默认字符集(除非在设置中另行指定)。

换句话说,在问题中提到的函数的发送行之前,加上类似下面的代码:

self.msg.content_subtype = "iso-8859-1"

不幸的是,这样做只会改变第一部分(上面函数中的msg_text)的编码。负责附加替代内容的函数似乎并没有使用这个encoding属性。我不确定这是不是正确的方法,但我通过子类化EmailMultiAlternatives来重写相关函数,结果看起来还不错。

class EmailMultiAlternativesWithEncoding(EmailMultiAlternatives):
    def _create_attachment(self, filename, content, mimetype=None):
        """
        Converts the filename, content, mimetype triple into a MIME attachment
        object. Use self.encoding when handling text attachments.
        """
        if mimetype is None:
            mimetype, _ = mimetypes.guess_type(filename)
            if mimetype is None:
                mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
        basetype, subtype = mimetype.split('/', 1)
        if basetype == 'text':
            encoding = self.encoding or settings.DEFAULT_CHARSET
            attachment = SafeMIMEText(smart_str(content,
                settings.DEFAULT_CHARSET), subtype, encoding)
            # original text being replaced above (not last argument)
            # attachment = SafeMIMEText(smart_str(content,
            #     settings.DEFAULT_CHARSET), subtype, settings.DEFAULT_CHARSET)
        else:
            # Encode non-text attachments with base64.
            attachment = MIMEBase(basetype, subtype)
            attachment.set_payload(content)
            Encoders.encode_base64(attachment)
        if filename:
            attachment.add_header('Content-Disposition', 'attachment',
                                  filename=filename)
        return attachment

我不确定“smart_str(content, settings.DEFAULT_CHARSET)”这一部分是否也应该引用“encoding”,而不是“settings.DEFAULT_CHARSET”,但这是处理消息正文文本的方式(django.core.mail.EmailMessage.message)。

正如我所说,我没有Outlook,所以无法实际测试与Outlook相关的部分,但似乎确实把两个部分的字符集都改成了iso-8859-1。

撰写回答