Python Send Plain Text, Html Content, Attached Files, Embedded Images Email Example

The python modulesmtplib provides functions to send emails to an SMTP server. This article will show you how to send plain text, Html content emails using the smtplib module, it also shows how to send emails with attached files and how to embedded images in your email.

1. Send Email Through SMTP Server Example Overview.

  1. This example will need an SMTP server setup and running. If you do not have one, you can read the article How To Connect Localhost Apache James With Thunderbird to download and set up one SMTP server using Apache James on your local computer.
  2. And then you can use Eclipse + PyDev plugin to develop the Python code of this example, please read the article How To Run Python In Eclipse With PyDev.
  3. The example python source file name is SendEmail.py. It contains 6 methods as below picture. We will introduce them one by one.
    python-send-email-example-source-file

2. Python Send Email Example Source Code.

Below is the source code of the SendEmail.py file. Please read the comments for a detailed explanation.

2.1 Import Modules This Example Needed.

import smtplib, ssl, os.path
from email import encoders
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email import charset

2.2 Method Return SMTP Server Connection Object.

''' This method will connect to a smtp mail server with provided server domain, port number and whether use ssl or not.
    It will return an object that represent the smtp server connection.
'''
def connect_smtp_server(server_domain, server_port, use_ssl=False):
    smtp_server = None
    if use_ssl == True:
        ctx = ssl.create_default_context('smtp server use ssl encruption demo', cafile=None, capath=None, cadata=None)
        # start ssl encryption from very beginning.
        smtp_server = smtplib.SMTP_SSL(server_domain, server_port, context=ctx)
        # or you can start tls after smtp server object is created as below.
        # smtp_server = smtplib.SMTP(server_domain, server_port)
        # smtp_server.starttls(context=ctx)
    else:
        smtp_server = smtplib.SMTP(server_domain, server_port)
        
    return smtp_server

2.3 Method That Sends Plain Text Email.

''' This method wil show you how to send plain text email via smtp server. '''
def send_plain_text_email(from_addr, to_addrs, email_subject, email_content):
    msg = MIMEText(email_content, 'plain', 'utf-8')
    msg['From'] = from_addr
    '''Because to_addrs is a tuple, so you need to join the tuple element to a string with comma separated,
       otherwise it will throw  AttributeError: 'tuple' object has no attribute 'encode' '''
    msg['To'] = ','.join(to_addrs)
    msg['Subject'] = Header(email_subject, 'utf-8').encode()
    smtp_server = connect_smtp_server('smtp.test.com', '25')
    smtp_server.send_message(msg, from_addr, to_addrs)
    print('Send plain text email complete.')

2.4 Method That Sends Multiple Part Html Content Email.

''' This method will send both plain text and html content in an email through smtp mail server. '''    
def send_multiple_part_plain_text_and_html_content_email(from_addr, to_addrs, email_subject, email_plain_text_content, email_html_content):
    msg_plain_text = MIMEText(email_plain_text_content, 'plain', 'utf-8')
    msg_html_content = MIMEText(email_html_content, 'html', 'utf-8')
    
    msg = MIMEMultipart("alternative")
    msg['From'] = from_addr
    '''Because to_addrs is a tuple, so you need to join the tuple element to a string with comma separated,
       otherwise it will throw  AttributeError: 'tuple' object has no attribute 'encode' '''
    msg['To'] = ','.join(to_addrs)
    msg['Subject'] = Header(email_subject, 'utf-8').encode()
    msg.attach(msg_plain_text)
    msg.attach(msg_html_content)
    
    smtp_server = connect_smtp_server('smtp.test.com', '25')
    smtp_server.send_message(msg, from_addr, to_addrs)
    print('Send multiple part plain text and html content email complete.')

2.5 Method That Send Attached Files Email.

''' This method will send email with attached files.'''    
def send_email_with_attached_files(from_addr, to_addrs, email_subject, email_content, attached_file_path_tuple):
    # create the MIMEMultipart object which contains both email content and attached files.
    msg = MIMEMultipart("")
    # set from, to and subject for the message.
    msg['From'] = from_addr
    '''Because to_addrs is a tuple, so you need to join the tuple element to a string with comma separated,
       otherwise it will throw  AttributeError: 'tuple' object has no attribute 'encode' '''
    msg['To'] = ','.join(to_addrs)
    msg['Subject'] = Header(email_subject, 'utf-8').encode()
    
    # create the message content object which is a plain text data.
    msg_content = MIMEText(email_content, 'plain', 'utf-8')
    msg.attach(msg_content)
    
    i = 0
    # loop in the attached file tuple.
    for attached_file_path in attached_file_path_tuple:
        if(os.path.isfile(attached_file_path)):
            # open each attached file. rb means the file is a readable binary file.
            # the open mode should be rb otherwise it will throw error message like UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf8 in position 96: invalid start byte
            attached_file = open(attached_file_path, 'rb')
            # create the attached file part which is a MIMEBase object.
            attached_part = MIMEBase("application", "octet-stream")
            attached_part.add_header("Content-Disposition", "attachment", filename=attached_file_path)
            attached_part.add_header('Content-ID', '<'+str(i)+'>')
            attached_part.add_header('X-Attachment-Id', str(i))
            i = i + 1
            attached_part.set_payload(attached_file.read())
            encoders.encode_base64(attached_part)
            print('Attached file is : ' + attached_file_path)
            msg.attach(attached_part)
            attached_file.close()
    
    smtp_server = connect_smtp_server('smtp.test.com', '25')
    smtp_server.send_message(msg, from_addr, to_addrs)
    print('Send email with attachment complete.')

2.6 Method That Send Embedded Image Email.

''' This method will send email contains images. '''   
def send_email_with_image(from_addr, to_addrs, email_subject, email_content, images_file_path_tuple):
    # below code will overwrite the header from Content-Transfer-Encoding: base64 to Content-Transfer-Encoding: 7bit, so that both the plain text and html content will be displayed
    # as normal content when you browse the email source code.
    charset.add_charset('utf-8', charset.SHORTEST, charset.UNKNOWN8BIT)
    
    # create the MIMEMultipart object which contains both email content and attached files.
    msg = MIMEMultipart("alternative")
    # set from, to and subject for the message.
    msg['From'] = from_addr
    '''Because to_addrs is a tuple, so you need to join the tuple element to a string with comma separated,
       otherwise it will throw  AttributeError: 'tuple' object has no attribute 'encode' '''
    msg['To'] = ','.join(to_addrs)
    msg['Subject'] = Header(email_subject, 'utf-8').encode()
    
    # create the message content object which is a plain text data.
    msg_content = MIMEText(email_content, 'plain', 'utf-8')
    msg.attach(msg_content)
    
    msg_html_content = MIMEText(email_content, 'html', 'utf-8')
    msg.attach(msg_html_content)
    
    i = 0

    # loop in the images file tuple, and attache images as email attachments one by one..
    for image_file_path in images_file_path_tuple:
        if(os.path.isfile(image_file_path)):
            # open each attached file. rb means the file is a readable binary file.
            # the open mode should be rb otherwise it will throw error message like UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf8 in position 96: invalid start byte
            image_file = open(image_file_path, 'rb')
            # create the attached file part which is a MIMEBase object.
            image_part = MIMEImage(image_file.read(), name=image_file_path)
            image_part.add_header('Content-ID', '<'+str(i)+'>')
            image_part.add_header("Content-Disposition", "in-line", filename=image_file_path)
            image_part.add_header('X-Attachment-Id', str(i))            
            # attache image part to the email.
            msg.attach(image_part)
            image_file.close()
            
            image_part_str = image_part.as_string()
            print('Attached image file is : ' + image_file_path + ', image part string : ' + image_part_str)
            i = i + 1
    
    # connect to smtp serve and send the email.
    smtp_server = connect_smtp_server('smtp.test.com', '25')
    smtp_server.send_message(msg, from_addr, to_addrs)
    print('Send email contains images complete.')

2.7 The Main Method That Tests All Above Methods.

You should replace the attached file’s path and images path with your own.

if __name__ == '__main__':
    # initiate some email variable with values.
    from_addr = "[email protected]"
    to_addrs = ('[email protected]','[email protected]')
    email_subject = 'Python send plain text email.'
    email_plain_text = 'Hello, this email send from jerry to admin'
    
    # send plain text email.
    send_plain_text_email(from_addr, to_addrs, email_subject, email_plain_text)

    # send plain text and html content email.
    email_subject = 'Python send html content email.'
    email_html_content = 'Hello, this email send from jerry to admin, you can click <a href="http://www.dev2qa.com">dev2qa.com</a> to access our website'
    send_multiple_part_plain_text_and_html_content_email(from_addr, to_addrs, email_subject, email_plain_text, email_html_content)

    # send email with attachments.
    email_subject = 'Python send attached files email.'
    email_content = 'This email contains attachments.'
    # attached_file_path_tuple = ('/home/zhaosong/WorkSpace/eclipse-inst', '/home/zhaosong/WorkSpace/eclipse-inst.ini', '/home/zhaosong/WorkSpace/example.pdf')
    attached_file_path_tuple = ('/Users/zhaosong/Documents/WorkSpace/EmployeeInfo.xls', '/Users/zhaosong/Documents/WorkSpace/e-book/Training Material/Mastering Regular Expressions, 3rd Edition.pdf')    
    send_email_with_attached_files(from_addr, to_addrs, email_subject, email_content, attached_file_path_tuple)

    # send email with embedded images, please notice that the img tag src attribute valueis cid:.
    email_subject = 'Python send embedded images email.'
    email_content = 'This email contains two images. Image one <br/> <img src="cid:0" width="100" height="100"><br/><br/>Image two <br/> <img src="cid:1">'
    # images_file_path_tuple = ('/home/zhaosong/WorkSpace/python-logo.png', '/home/zhaosong/WorkSpace/python-docker.jpeg')    
    images_file_path_tuple = ('/Users/zhaosong/Documents/create custom error controller class to display detail error information.png', '/Users/zhaosong/Documents/custom server 500 error page .png')        
    send_email_with_image(from_addr, to_addrs, email_subject, email_content, images_file_path_tuple)

When you run the above example code, you can use thunderbird to receive all the example emails in it.

1 thought on “Python Send Plain Text, Html Content, Attached Files, Embedded Images Email Example”

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.