8

I am trying to get images shown as part of the mail sent with Python. There is a example on Python docs which is not working.

from datetime import datetime
import sys

import smtplib

from email.message import EmailMessage
from email.headerregistry import Address
from email.utils import make_msgid



from email.mime.image import MIMEImage

attachment = '/user/file/test.png'


import email.policy
msg = EmailMessage()
msg['To'] = Address("Srujan", '[email protected]')
msg['From'] = Address("Srujan", '[email protected]')
msg['Subject'] = "Nice message goes with it "+str(datetime.now())
id = make_msgid()
msg.set_type('text/html')
msg.set_content(" This is the Data Message that we want to send")
html_msg = "<br> <b><u> This is the Text .... </u></b><br> <img src='cid:{image_id}' />".format(image_id=id[1:-1])
msg.add_alternative(html_msg, subtype="html")
image_data = open(attachment, "rb")
image_mime = MIMEImage(image_data.read())
image_data.close()
msg.add_attachment(image_mime,   cid=id, filename = "myown.png" ,)


try:
    with smtplib.SMTP('example.com') as s:
        s.ehlo()
        s.starttls()
        s.ehlo()
        
        s.send_message(msg)
        s.close()
    print("Email sent!")
except:
    print("Unable to send the email. Error: ", sys.exc_info()[0])
    raise

I noticed that the last part is a message/rfc822, which then contains the image/png.

To: Srujan <"[email protected]"> From: Srujan
<"[email protected]"> Subject: Nice message goes with it 2016-01-21
17:39:23.642762 MIME-Version: 1.0 Content-Type: multipart/mixed;
boundary="===============3463325241650937591=="

--===============3463325241650937591==
Content-Type: multipart/alternative; boundary="===============0739224896516732414=="

--===============0739224896516732414==
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit

 This is the Data Message that we want to send

--===============0739224896516732414==
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: 7bit MIME-Version: 1.0

<br> <b><u> This is the Text .... </u></b><br> <img
src='cid:20160122013923.64205.76661' />

--===============0739224896516732414==--

--===============3463325241650937591==
Content-Type: message/rfc822
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="myown.png"
Content-ID: <20160122013923.64205.76661>
MIME-Version: 1.0

Content-Type: image/png
MIME-Version: 1.0
Content-Transfer-Encoding: base64

iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAIAAAAVFBUnAAAABmJLR0QA/wD/AP+gvaeTAAAgAElE
QVR4nOzdd2BUVfYH8HNfmZJAChAEQ1FAqoKgCAsWLEgTG4gKP11FQNG1AauIgGBXVhRUsCK4i6gI

Now the attached message has two content-type values. Email comes with just text and with no image.

I have done it successfully with MultiPart class, but looking to achieve that with EmailMessage.

1 Answer 1

11

It is really a pity that Python docs on this subject are incomplete/wrong. I wanted to do the same thing and got the same problem (attachments got two content-types)

Solved the problem by using the attach() method instead of add_attachment() on EmailMessage objects. The only caveat is that you have to transform the EmailMessage to multipart/mixed type before doing this. An example:

from smtplib import SMTP
from email.message import EmailMessage
from email.mime.text import MIMEText
from email.headerregistry import Address
from ssl import SSLContext, PROTOCOL_TLSv1_2

# Creating and populating email data:
msg = EmailMessage()
msg['From'] = Address(display_name='Recipient', addr_spec='[email protected]')
msg['To'] = Address(display_name='Sender', addr_spec='[email protected]')
msg['Subject'] = 'An email for you'    
msg.set_content('This should be in the email body')  
# It is possible to use msg.add_alternative() to add HTML content too  

# Attaching content:
att = MIMEText('This should be in an attached file') # Or use MIMEImage, etc
# The following line is to control the filename of the attached file
att.add_header('Content-Disposition', 'attachment', filename='attachment.txt')
msg.make_mixed() # This converts the message to multipart/mixed
msg.attach(att) # Don't forget to convert the message to multipart first!

# Sending the email:
with SMTP(host='smtp.example.org', port=587) as smtp_server:
    try:
        # You can choose SSL/TLS encryption protocol to use as shown
        # or just call starttls() without parameters
        smtp_server.starttls(context=SSLContext(PROTOCOL_TLSv1_2))
        smtp_server.login(user='[email protected]', password='password')
        smtp_server.send_message(msg)

    except Exception as e:                
        print('Error sending email. Details: {} - {}'.format(e.__class__, e))
Sign up to request clarification or add additional context in comments.

2 Comments

I tried this route but gmail kept rejecting it: 'Messages missing a valid address in From: 550 5.7.1 header, or having no From: header, are not accepted'. I did not even try over a Flask app. Let us know if you may have an update
Unfortunately long time ago I stopped developing in Python. I'm afraid I can't help you this time 😢

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.