用Python/Django创建带图像的MIME邮件模板
在我的网页应用中,我偶尔会使用一个可以重复使用的邮件发送工具来发送邮件,像这样:
user - self.user
subject = ("My subject")
from = "me@mydomain.com"
message = render_to_string("welcomeEmail/welcome.eml", {
"user" : user,
})
send_mail(subject, message, from, [email], priority="high" )
我想发送一封包含嵌入图片的邮件,所以我尝试在邮件客户端中制作邮件,查看源代码,然后把它放到我的模板(welcome.eml)里,但我发现发送出去后,在邮件客户端中显示得不对。
有没有人知道一个简单的方法,可以让我创建带有内嵌图片的邮件模板,并且发送后能正确显示?
3 个回答
使用文本替代的版本
Jarret Hardie 的回答提到:“你可以传递纯文本来创建这个消息的替代部分。”不过,我发现这样做会导致 Gmail(虽然 Outlook 不会)将电子邮件的两个部分一个接一个地显示出来,就像这里描述的那样。
正如那里和其他地方推荐的,我简单地使用了 EmailMultiAlternatives
。最终的代码大致如下:
from django.core.mail import EmailMultiAlternatives
from email.mime.image import MIMEImage
# Load the image you want to send as bytes
img_data = open('logo.jpg', 'rb').read()
# 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
html_content = '<p>Hello <img src="cid:myimage" /></p>'
text_content = 'Hello'
# Configure an EmailMultiAlternatives
msg = EmailMultiAlternatives('Subject Line', text_content, 'foo@bar.com', ['bar@foo.com'])
msg.attach_alternative(html_content)
# 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
msg.attach(img)
# Finally, send the whole thing.
msg.send()
我在使用Jarret的配方时遇到了问题,特别是在Django 1.10上,出现了MIME和编码错误,这些错误与不同的方式附加MIME数据有关。
这里有一个简单的多部分事务模板,用于发送电子邮件,并且里面嵌入了一个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='noreply@example.com',
to=['someone@example.com',]
)
email.attach_alternative(html_body, "text/html")
email.mixed_subtype = 'related'
email.attach(img)
email.send(fail_silently=False)
更新
非常感谢 Saqib Ali,在我回复的近五年后重新提起了这个老问题。
我当时给出的指示现在已经不再有效。我猜这几年Django有了一些改进,导致 send_mail()
现在只支持纯文本。不管你在内容里写什么,最终都会以纯文本的形式发送。
最新的 Django文档 解释说, send_mail()
其实只是用来方便地创建一个 django.core.mail.EmailMessage
类的实例,然后调用这个实例的 send()
方法。 EmailMessage
对于内容部分有这样的说明,这也解释了我们在2014年看到的结果:
内容:正文文本。这应该是一个纯文本消息。
... 文档稍后提到 ...
默认情况下,EmailMessage中内容部分的MIME类型是“text/plain”。最好保持这个设置。
说得不错(我承认我没有花时间去调查为什么2009年的指示有效——我在2009年测试过——或者是什么时候改变的)。Django确实提供了一个 django.core.mail.EmailMultiAlternatives
类,并且有相关的 文档,可以更方便地发送同一消息的纯文本和HTML版本。
这个问题的情况稍微不同。我们并不是想要添加一个替代内容,而是想要在其中一个替代内容中添加相关部分。在HTML版本中(无论你是否有纯文本版本),我们想要嵌入一个图片数据部分。不是内容的替代视图,而是HTML正文中引用的相关内容。
发送嵌入的图片仍然是可能的,但我没有看到使用 send_mail
的简单方法。是时候放弃这个方便的函数,直接实例化一个 EmailMessage
了。
这是对之前示例的更新:
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, 'foo@bar.com', ['bar@foo.com'])
msg.attach(html_part) # Attach the raw MIMEBase descendant. This is a public method on EmailMessage
msg.send()
2009年的原始回复:
要发送带有嵌入图片的电子邮件,可以使用Python内置的电子邮件模块来构建MIME部分。
以下代码应该可以实现:
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")
实际上,你可能想要同时发送HTML和纯文本的替代内容。在这种情况下,使用MIMEMultipart创建“相关”的MIME类型容器作为根。然后创建另一个子类型为“alternative”的MIMEMultipart,并将一个MIMEText(子类型为html)和一个MIMEText(子类型为plain)附加到替代部分。最后,将图片附加到相关的根部分。