I am using python to generate and send email messages. I want the messages to include both
- embedded images (this post shows how)
- attachments
Currently, the code below does all of this, except that the attachments are not 100% showing up. In Outlook, there is no way to see them. In Gmail, when you see the mail in your inbox it seems to have no attachment, but when you open the mail, there is the attachment at the bottom.
How can I get the attachments to show up consistently?
Following is my current code. I'll also show below that a comparison between the header seem from my gmail inbox of an email sent with this, vs one where everything works as expected.
import smtplib
from os import getcwd, path
from config import mailer_settings
from email.message import EmailMessage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
from email.utils import formatdate
from mimetypes import guess_type
def send_mail(send_from, send_to, subject, message, message_html="", file_loc: str = None, locs=[], cids=[],
use_tls=True):
"""Compose and send email with provided info and attachments.
Args:
send_from (str): from name
send_to (list[str]): to name(s)
subject (str): message title
message (str): message body
message_html: message body with html
file_loc (list[str]): path of file to be attached to email
locs: list[str]: list of file locations for embedded images
cids: list: list of generated (email.utils.make_msgid) cids for embedded images
use_tls (bool): use TLS mode
"""
# --- Method 1: can't get embedded pictures to work ---
# msg = MIMEMultipart('alternative')
# part1 = MIMEText(message, 'plain')
# part2 = MIMEText(message_html, 'html')
# msg.attach(part1)
# msg.attach(part2)
# --- Method 2 currently best ---
msg = EmailMessage()
msg.set_content(message)
msg.add_alternative(message_html, subtype='html')
# --- Method 3:
# from (https://stackoverflow.com/questions/35925969/python-sent-mime-email-attachments-not-showing-up-in-mail-live)
# not sure how to get it to work with embedded pictures, since MIMEImage is now deprecated ---
# Create the root message and fill in the from, to, and subject headers
b = False
if b:
msg = MIMEMultipart('related')
msg.preamble = 'This is a multi-part message in MIME format.'
# Encapsulate the plain and HTML versions of the message body in an
# 'alternative' part, so message agents can decide which they want to display.
msgAlternative = MIMEMultipart('alternative')
msg.attach(msgAlternative)
msgText = MIMEText('This is the alternative plain text message.')
msgAlternative.attach(msgText)
# We reference the image in the IMG SRC attribute by the ID we give it below
msgText = MIMEText('<b>Some <i>HTML</i> text</b> and an image.<br><img src="cid:image1"><br>Nifty!', 'html')
msgAlternative.attach(msgText)
# This example assumes the image is in the current directory
fp = open(getcwd() + '\\mail\\example_pdf.ong', 'rb')
# :( MIMEImage is deprecated
# msgImage = MIMEImage(fp.read())
fp.close()
# Define the image's ID as referenced above
# msgImage.add_header('Content-ID', '<image1>')
# msg.attach(msgImage)
# --- end Method 3 ---
msg['From'] = send_from
msg['To'] = send_to
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
# now open the image and attach it to the email
for loc, cid in zip(locs, cids):
with open(loc, 'rb') as img:
# know the Content-Type of the image
maintype, subtype = guess_type(img.name)[0].split('/')
# attach it
msg.get_payload()[1].add_related(img.read(), maintype=maintype, subtype=subtype, cid=cid)
if file_loc:
mimetype, encoding = guess_type(file_loc)
mimetype = mimetype.split('/', 1)
fp = open(file_loc, 'rb')
attachment = MIMEBase(mimetype[0], mimetype[1])
attachment.set_payload(fp.read())
fp.close()
encoders.encode_base64(attachment)
attachment.add_header('Content-Disposition', 'attachment',
filename=path.basename(file_loc))
msg.attach(attachment)
server = mailer_settings["server"]
port = mailer_settings["port"]
username = mailer_settings["username"]
password = mailer_settings["password"]
smtp = smtplib.SMTP(server, port)
if use_tls:
smtp.starttls()
smtp.login(username, password)
smtp.sendmail(send_from, send_to, msg.as_string())
print("message sent!")
smtp.quit()
The Header in my emails:
--===============0780945104962264622==
Content-Type: application/pdf
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="Ecoemballage - test_product_01a.pdf"
--===============0780945104962264622==--
Header from a Microsoft email that is showing it attachment like it should:
--=-FabH5jV6gYAblpCdlHoDfQ==
Content-Type: application/octet-stream; name=221885190882013.pdf
Content-Disposition: attachment; filename=221885190882013.pdf
Content-Transfer-Encoding: base64
--=-FabH5jV6gYAblpCdlHoDfQ==--