Создание шаблона электронной почты MIME с изображениями для отправки с помощью python / django

В моем веб-приложении я время от времени отправляю электронные письма, используя повторно используемую почтовую программу, например:

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), но мне не удалось получить его для рендеринга. правильно в почтовых клиентах при его отправке.

Кто-нибудь знает, как мне легко создавать почтовые шаблоны в формате MIME со встроенными изображениями, которые будут правильно отображаться при их отправке?

3 ответа

Решение

Обновить

Большое спасибо Saqib Ali за то, что он воскресил этот старый вопрос спустя почти 5 лет после моего ответа.

Инструкции, которые я дал в то время, больше не работают. Я подозреваю, что за прошедшие годы в Django произошли некоторые улучшения, которые означают, что send_mail() обеспечивает соблюдение простого текста. Независимо от того, что вы поместите в контент, он всегда будет доставлен в виде простого текста.

В самой последней документации по Django объясняется, что send_mail() на самом деле просто удобство для создания экземпляра django.core.mail.EmailMessage класс, а затем позвонив send() в этом случае. EmailMessage имеет это примечание для параметра body, который объясняет результаты, которые мы видим сейчас в 2014 году:

тело: основной текст. Это должно быть текстовое сообщение.

... несколько позже в документах...

По умолчанию MIME-тип параметра body в EmailMessage - "text/plain". Хорошей практикой является оставить это в покое.

Справедливости ради (признаюсь, я не потратил время на то, чтобы выяснить, почему сработали инструкции 2009 года - я проверял их еще в 2009 году - или когда они изменились). Джанго предоставляет и документирует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 для создания "связанного" контейнера mimetype в качестве корневого. Затем создайте другой MIMEMultipart с подтипом "альтернатива" и присоедините MIMEText (подтип html) и MIMEText (подтип plain) к альтернативной части. Затем прикрепите изображение к соответствующему корню.

У меня были проблемы с рецептом Джаррета на 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)

Версия с использованием текстовой альтернативы

В ответе Джаррета Харди говорится: «Вы можете передать обычный текст, чтобы создать альтернативную часть для этого сообщения». Однако я обнаружил, что это приведет к тому, что 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()
Другие вопросы по тегам