Sending Multipart HTML Emails Which Contain Embedded Images

Sending Multipart html emails which contain embedded images

For Python versions 3.4 and above.

The accepted answer is excellent, but only suitable for older Python versions (2.x and 3.3). I think it needs an update.

Here's how you can do it in newer Python versions (3.4 and above):

from email.message import EmailMessage
from email.utils import make_msgid
import mimetypes

msg = EmailMessage()

# generic email headers
msg['Subject'] = 'Hello there'
msg['From'] = 'ABCD <abcd@xyz.com>'
msg['To'] = 'PQRS <pqrs@xyz.com>'

# set the plain text body
msg.set_content('This is a plain text body.')

# now create a Content-ID for the image
image_cid = make_msgid(domain='xyz.com')
# if `domain` argument isn't provided, it will
# use your computer's name

# set an alternative html body
msg.add_alternative("""\
<html>
<body>
<p>This is an HTML body.<br>
It also has an image.
</p>
<img src="cid:{image_cid}">
</body>
</html>
""".format(image_cid=image_cid[1:-1]), subtype='html')
# image_cid looks like <long.random.number@xyz.com>
# to use it as the img src, we don't need `<` or `>`
# so we use [1:-1] to strip them off

# now open the image and attach it to the email
with open('path/to/image.jpg', 'rb') as img:

# know the Content-Type of the image
maintype, subtype = mimetypes.guess_type(img.name)[0].split('/')

# attach it
msg.get_payload()[1].add_related(img.read(),
maintype=maintype,
subtype=subtype,
cid=image_cid)

# the message is ready now
# you can write it to a file
# or send it using smtplib

Python - send mail to GMAIL with embedded images

You need to attach it and reference it in the HTML. I.e. attach a message with a HTML img that sources the image you've attached.

I see you are well underway, working with the same modules and have a working code, so you should be able get it done with this snippet of code below:

.....
import os
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
.....
# Get the files/images you want to add.
img_dir = "/images"
images = [os.path.join(img_dir, i) for i in os.listdir(img_dir)]

# Added a enumerate loop around the whole procedure.
# Reference to cid:image_id_"j", which you will attach to the email later.
for j, val in enumerate(images):
msgText = MIMEText('<br> <img src="cid:image_id_{}"> </br>'.format(j), 'html')
msgAlternative.attach(msgText)

with open('{}'.format(val), "rb") as attachment:
msgImage = MIMEImage(attachment.read())

# Define the image's ID with counter as you will reference it.
msgImage.add_header('Content-ID', '<image_id_{}>'.format(j))
msgRoot.attach(msgImage)

Embed picture in email

You are going through royal pains to construct a valid MIME message in msg, then ditching it and sending a simple string email_message instead.

You should probably begin by understanding what the proper MIME structure looks like. A multipart message by itself has no contents at all, you have to add a text part if you want a text part.

The following is an edit of your script with the missing pieces added. I have not attempted to send the resulting message. However, see below for a modernized version.

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText # Added
from email.mime.image import MIMEImage

attachment = 'bob.jpg'

msg = MIMEMultipart()
msg["To"] = to_addr
msg["From"] = from_addr
msg["Subject"] = subject

msgText = MIMEText('<b>%s</b><br/><img src="cid:%s"/><br/>' % (body, attachment), 'html')
msg.attach(msgText) # Added, and edited the previous line

with open(attachment, 'rb') as fp:
img = MIMEImage(fp.read())
img.add_header('Content-ID', '<{}>'.format(attachment))
msg.attach(img)

print(msg.as_string()) # or go ahead and send it

(I also cleaned up the HTML slightly.)

Since Python 3.6, Python's email library has been upgraded to be more modular, logical, and orthogonal (technically since 3.3 already really, but in 3.6 the new version became the preferred one). New code should avoid the explicit creation of individual MIME parts like in the above code, and probably look more something like

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

attachment = 'bob.jpg'

msg = EmailMessage()
msg["To"] = to_addr
msg["From"] = from_addr
msg["Subject"] = subject

attachment_cid = make_msgid()

msg.set_content(
'<b>%s</b><br/><img src="cid:%s"/><br/>' % (
body, attachment_cid[1:-1]), 'html')

with open(attachment, 'rb') as fp:
msg.add_related(
fp.read(), 'image', 'jpeg', cid=attachment_cid)

# print(msg.as_string()), or go ahead and send

You'll notice that this is quite similar to the "asparagus" example from the email examples documentation in the Python standard library.

How to add multiple embedded images to an email in Python?

If I'm able to guess what you are trying to ask, the solution is simply to generate a unique cid for each image.

from email.message import EmailMessage
from email.utils import make_msgid
# import mimetypes

msg = EmailMessage()

msg["Subject"] = "Hello there"
msg["From"] = "ABCD <abcd@example.com>"
msg["To"] = "PQRS <pqrs@example.org>"

# create a Content-ID for each image
image_cid = [make_msgid(domain="example.com")[1:-1],
make_msgid(domain="example.com")[1:-1],
make_msgid(domain="example.com")[1:-1]]

msg.set_content("""\
<html>
<body>
<p>This is an HTML body.<br>
It also has three images.
</p>
<img src="cid:{image_cid[0]}"><br/>
<img src="cid:{image_cid[1]}"><br/>
<img src="cid:{image_cid[2]}">
</body>
</html>
""".format(image_cid=image_cid), subtype='html')

for idx, imgtup in enumerate([
("path/to/first.jpg", "jpeg"),
("file/name/of/second.png", "png"),
("path/to/third.gif", "gif")]):
imgfile, imgtype = imgtup
with open(imgfile, "rb") as img:
msg.add_related(
img.read(),
maintype="image",
subtype=imgtype,
cid=f"<{image_cid[idx]}>")

# The message is ready now.
# You can write it to a file
# or send it using smtplib

Kudos for using the modern EmailMessage API; we still see way too many questions which blindly copy/paste the old API from Python <= 3.5 with MIMEMultipart etc etc.

I took out the mimetypes image format quessing logic in favor of spelling out the type of each image in the code. If you need Python to guess, you know how to do that, but for a small static list of images, it seems to make more sense to just specify each, and avoid the overhead as well as the unlikely but still not impossible problem of having the heuristics guess wrong.

I'm guessing your images will all use the same format, and so you could actually simply hardcode subtype="png" or whatever.

It should hopefully be obvious how to add more per-image information into the loop over image tuples, though if your needs go beyond the trivial, you'll probably want to encapsulate the image and its various attributes into a simple class.

Your message apparently makes no sense for a recipient who cannot access the HTML part, so I took out the bogus text/plain part you had. You were effectively sending a different message entirely to recipients whose preference is to view plain text over HTML; if that was genuinely your intent, please stop it. If you are unable to provide the same information in the plain text version as in the HTML version, at least don't make it look to those recipients like you had nothing of importance to say in the first place.

Tangentially, please don't fake email addresses of domains you don't own. You will end up tipping off the spammers and have them trying to send unsolicited messages to an innocent third party. Always use IANA-reserved domains like example.com, example.org etc which are guaranteed to never exist in reality. I edited your question to fix this.

send html email with embedded image using python script

I figured out the issue. Here is the updated code for your referance.

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.MIMEImage import MIMEImage

msg = MIMEMultipart('related')
msg['Subject'] = "My text dated %s" % (today)
msg['From'] = sender
msg['To'] = receiver

html = """\
<html>
<head></head>
<body>
<img src="cid:image1" alt="Logo" style="width:250px;height:50px;"><br>
<p><h4 style="font-size:15px;">Some Text.</h4></p>
</body>
</html>
"""
# Record the MIME types of text/html.
part2 = MIMEText(html, 'html')

# Attach parts into message container.
msg.attach(part2)

# This example assumes the image is in the current directory
fp = open('logo.png', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()

# Define the image's ID as referenced above
msgImage.add_header('Content-ID', '<image1>')
msg.attach(msgImage)

# Send the message via local SMTP server.
mailsrv = smtplib.SMTP('localhost')
mailsrv.sendmail(sender, receiver, msg.as_string())
mailsrv.quit()

How to embed a linked image into a python email

The example you linked were properly formatted HTML Emails where add_alternative() was used to supply the HTML portion of the email. You excluded this in what you've written. If you include an actual HTML body for your email, then all you need to do is wrap the image in an anchor(link) element with the url you're trying to link to.

(Adapted from your linked question)

msg.add_alternative("""\
<html>
<body>
<p>Click the Image below to visit our site!</p>
<a href="www.google.com"><img src="cid:{image_cid}"></a>
</body>
</html>
""".format(image_cid=image_cid[1:-1]), subtype='html')


Edit

Don't have Python 2 around to test, but the following code from the Accepted Answer on that same thread (which was indicated as being Python 2.x compatible) presumably should work.

msgAlternative = MIMEMultipart('alternative')
msgRoot.attach(msgAlternative)

msgText = MIMEText('<b>Some <i>HTML</i> text</b> and an image.<br><a href="www.google.com"><img src="cid:image1"></a><br>Nifty!', 'html')
msgAlternative.attach(msgText)

Again, the point here being that you embed the image via html, which also allows you apply anchor tags (and basically any other styling you would like).

embedding image in html email

Try to insert it directly, this way you can insert multiple images at various locations in the email.

<img src="data:image/jpg;base64,{{base64-data-string here}}" />

And to make this post usefully for others to:
If you don't have a base64-data string, create one easily at:
http://www.motobit.com/util/base64-decoder-encoder.asp from a image file.

Email source code looks something like this, but i really cant tell you what that boundary thing is for:

 To: email@email.de
Subject: ...
Content-Type: multipart/related;
boundary="------------090303020209010600070908"

This is a multi-part message in MIME format.
--------------090303020209010600070908
Content-Type: text/html; charset=ISO-8859-15
Content-Transfer-Encoding: 7bit

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>

<meta http-equiv="content-type" content="text/html; charset=ISO-8859-15">
</head>
<body bgcolor="#ffffff" text="#000000">
<img src="cid:part1.06090408.01060107" alt="Sample Image">
</body>
</html>

--------------090303020209010600070908
Content-Type: image/png;
name="moz-screenshot.png"
Content-Transfer-Encoding: base64
Content-ID: <part1.06090408.01060107>
Content-Disposition: inline;
filename="moz-screenshot.png"

[base64 image data here]

--------------090303020209010600070908--

//EDIT: Oh, i just realize if you insert the first code snippet from my post to write an email with thunderbird, thunderbird automatically changes the html code to look pretty much the same as the second code in my post.



Related Topics



Leave a reply



Submit