创建一个带有图像的 MIME 电子邮件模板,用 python/django 发送

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1633109/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 22:45:02  来源:igfitidea点击:

creating a MIME email template with images to send with python / django

pythondjangoemailmime

提问by Tristan Brotherton

In my web application I send emails occasionally using a reusable mailer application like this:

在我的 Web 应用程序中,我偶尔会使用像这样的可重用邮件应用程序发送电子邮件:

user - self.user
subject = ("My subject")
from = "[email protected]"
message = render_to_string("welcomeEmail/welcome.eml", { 
                "user" : user,
                })
send_mail(subject, message, from, [email], priority="high" )

I want to send an email with embedded images in it, so I tried making the mail in a mail client, viewing the source, and putting it into my template (welcome.eml), but I've been unable to get it to render correctly in mail clients when its sent.

我想发送一封带有嵌入图像的电子邮件,所以我尝试在邮件客户端中制作邮件,查看源代码,并将其放入我的模板 (welcome.eml),但我一直无法让它呈现发送时在邮件客户端中正确。

Does anyone know of an easy way for me to create mime encoded mail templates with inline images that will render correctly when I send them?

有没有人知道一种简单的方法可以让我创建带有内嵌图像的 mime 编码邮件模板,这些图像在我发送时会正确呈现?

回答by Jarret Hardie

Update

更新

Many thanks to Saqib Alifor resurrecting this old question nearly 5 years after my reply.

非常感谢Saqib Ali在我回复将近 5 年后重新提出这个老问题。

The instructions I gave at the time no longer work. I suspect there have been some improvements to Django in the intervening years which mean that send_mail()enforces plain text. No matter what you put in the content, it will always be delivered as plain text.

我当时给出的指示不再有效。我怀疑在此期间 Django 有一些改进,这意味着send_mail()强制执行纯文本。无论您在内容中放入什么内容,它都将始终以纯文本形式传送。

The most recent Django documentationexplains that send_mail()is really just a convenience for creating an instance of the django.core.mail.EmailMessageclass, and then calling send()on that instance. EmailMessagehas this note for the body parameter, which explains the results we're seeing now in 2014:

最新的Django 文档解释说,这send_mail()实际上只是为了方便创建django.core.mail.EmailMessage类的实例,然后调用send()该实例。EmailMessage有关于 body 参数的注释,它解释了我们现在在 2014 年看到的结果:

body: The body text. This should be a plain text message.

... somewhat later in the docs ...

By default, the MIME type of the body parameter in an EmailMessage is "text/plain". It is good practice to leave this alone.

正文:正文。这应该是纯文本消息。

......稍后在文档中......

默认情况下,EmailMessage 中 body 参数的 MIME 类型为“text/plain”。最好不要管它。

Fair enough (I confess I haven't taken the time to investigate why the 2009 instructions worked - I did test them back in 2009 - or when it changed). Django does provide, and document, a django.core.mail.EmailMultiAlternativesclass to make it easier for sending a plain text and HTML representation of the same message.

足够公平(我承认我没有花时间去调查为什么 2009 年的指令有效——我确实在 2009 年测试过它们——或者当它改变时)。Django 确实提供和document一个django.core.mail.EmailMultiAlternatives类,使发送同一消息的纯文本和 HTML 表示更容易。

The case in this question is slightly different. We're not seeking to append an alternative per se, but to append relatedparts to one of the alternatives. Within the HTML version (and it doesn't matter if you have or omit the plain text version), we want to embed an image data part. Not an alternative view of the content, but relatedcontent that is referenced in the HTML body.

这个问题的情况略有不同。我们并不寻求附加替代方案本身,而是将相关部分附加到其中一个替代方案。在 HTML 版本中(无论是否有纯文本版本都没有关系),我们想要嵌入图像数据部分。不是内容的替代视图,而是HTML 正文中引用的相关内容。

Sending an embedded image is still possible, but I don't see a straightforward way to do it using send_mail. It's time to dispense with the convenience function and to instantiate an EmailMessagedirectly.

仍然可以发送嵌入的图像,但我没有看到使用send_mail. 是时候放弃便利功能并EmailMessage直接实例化 an 了。

Here's an update to the previous example:

这是对上一个示例的更新:

from django.core.mail import EmailMessage
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

# Load the image you want to send as bytes
img_data = open('logo.jpg', 'rb').read()

# Create a "related" message container that will hold the HTML 
# message and the image. These are "related" (not "alternative")
# because they are different, unique parts of the HTML message,
# not alternative (html vs. plain text) views of the same content.
html_part = MIMEMultipart(_subtype='related')

# Create the body with HTML. Note that the image, since it is inline, is 
# referenced with the URL cid:myimage... you should take care to make
# "myimage" unique
body = MIMEText('<p>Hello <img src="cid:myimage" /></p>', _subtype='html')
html_part.attach(body)

# Now create the MIME container for the image
img = MIMEImage(img_data, 'jpeg')
img.add_header('Content-Id', '<myimage>')  # angle brackets are important
img.add_header("Content-Disposition", "inline", filename="myimage") # David Hess recommended this edit
html_part.attach(img)

# Configure and send an EmailMessage
# Note we are passing None for the body (the 2nd parameter). You could pass plain text
# to create an alternative part for this message
msg = EmailMessage('Subject Line', None, '[email protected]', ['[email protected]'])
msg.attach(html_part) # Attach the raw MIMEBase descendant. This is a public method on EmailMessage
msg.send()


Original reply from 2009:

2009 年的原始回复:

To send an e-mail with embedded images, use python's built-in email module to build up the MIME parts.

要发送带有嵌入图像的电子邮件,请使用 python 的内置电子邮件模块来构建 MIME 部分。

The following should do it:

以下应该这样做:

from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

# Load the image you want to send at bytes
img_data = open('logo.jpg', 'rb').read()

# Create a "related" message container that will hold the HTML 
# message and the image
msg = MIMEMultipart(_subtype='related')

# Create the body with HTML. Note that the image, since it is inline, is 
# referenced with the URL cid:myimage... you should take care to make
# "myimage" unique
body = MIMEText('<p>Hello <img src="cid:myimage" /></p>', _subtype='html')
msg.attach(body)

# Now create the MIME container for the image
img = MIMEImage(img_data, 'jpeg')
img.add_header('Content-Id', '<myimage>')  # angle brackets are important
msg.attach(img)

send_mail(subject, msg.as_string(), from, [to], priority="high")

In reality, you'll probably want to send the HTML along with a plain-text alternative. In that case, use MIMEMultipart to create the "related" mimetype container as the root. Then create another MIMEMultipart with the subtype "alternative", and attach both a MIMEText (subtype html) and a MIMEText (subtype plain) to the alternative part. Then attach the image to the related root.

实际上,您可能希望将 HTML 与纯文本替代项一起发送。在这种情况下,使用 MIMEMultipart 创建“相关”的 mimetype 容器作为根。然后使用子类型“alternative”创建另一个 MIMEMultipart,并将 MIMEText(子类型 html)和 MIMEText(子类型普通)附加到替代部分。然后将图像附加到相关的根。

回答by Escher

I was having trouble with Jarret's recipe on Django 1.10 - was getting MIME and encoding errors for the various ways you can attach MIME data.

我在使用 Jarret 在 Django 1.10 上的配方时遇到了问题 - 为附加 MIME 数据的各种方式获取 MIME 和编码错误。

Here's a simple multipart transactional template for an email with an embedded coupon_imagefile object that works on django 1.10:

这是一个简单的多部分事务模板,用于带有嵌入式coupon_image文件对象的电子邮件,适用于 django 1.10:

from django.core.mail import EmailMultiAlternatives
from email.mime.image import MIMEImage

def send_mail(coupon_image):
    params = {'foo':'bar'} # create a template context
    text_body = render_to_string('coupon_email.txt', params)
    html_body = render_to_string('coupon_email.html', params)
    img_data = coupon_image.read() #should be a file object, or ImageField
    img = MIMEImage(img_data)
    img.add_header('Content-ID', '<coupon_image>')
    img.add_header('Content-Disposition', 'inline', filename="coupon_image")

    email = EmailMultiAlternatives(
        subject="Here's your coupon!",
        body=text_body,
        from_email='[email protected]',
        to=['[email protected]',]
    )

    email.attach_alternative(html_body, "text/html")
    email.mixed_subtype = 'related'
    email.attach(img)

    email.send(fail_silently=False)