发送包含嵌入图像的多部分 html 电子邮件

我一直在使用 python 中的 email 模块,但是我想知道如何嵌入包含在 html 中的图像。

例如,如果身体是

<img src="../path/image.png"></img>

我想嵌入到电子邮件的 Image png,和 src属性应该替换为 content-id。有人知道怎么做吗?

140325 次浏览

Here is an example I found.

Recipe 473810: Send an HTML email with embedded image and plain text alternate:

HTML is the method of choice for those wishing to send emails with rich text, layout and graphics. Often it is desirable to embed the graphics within the message so recipients can display the message directly, without further downloads.

Some mail agents don't support HTML or their users prefer to receive plain text messages. Senders of HTML messages should include a plain text message as an alternate for these users.

This recipe sends a short HTML message with a single embedded image and an alternate plain text message.

# Send an HTML email with an embedded image and a plain text message for
# email clients that don't want to display the HTML.


from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage


# Define these once; use them twice!
strFrom = 'from@example.com'
strTo = 'to@example.com'


# Create the root message and fill in the from, to, and subject headers
msgRoot = MIMEMultipart('related')
msgRoot['Subject'] = 'test message'
msgRoot['From'] = strFrom
msgRoot['To'] = strTo
msgRoot.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')
msgRoot.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('test.jpg', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()


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


# Send the email (this example assumes SMTP authentication is required)
import smtplib
smtp = smtplib.SMTP()
smtp.connect('smtp.example.com')
smtp.login('exampleuser', 'examplepass')
smtp.sendmail(strFrom, strTo, msgRoot.as_string())
smtp.quit()

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

Code working

    att = MIMEImage(imgData)
att.add_header('Content-ID', f'<image{i}.{imgType}>')
att.add_header('X-Attachment-Id', f'image{i}.{imgType}')
att['Content-Disposition'] = f'inline; filename=image{i}.{imgType}'
msg.attach(att)

I realized how painful some of the things are with SMTP and email libraries and I thought I have to do something with it. I made a library that makes embedding images to HTML way easier:

from redmail import EmailSender
email = EmailSender(host="<SMTP HOST>", port=0)


email.send(
sender="me@example.com",
receivers=["you@example.com"]
subject="An email with image",
html="""
<h1>Look at this:</h1>
\{\{ my_image }}
""",
body_images={
"my_image": "path/to/image.png"
}
)

Sorry for promotion but I think it's pretty awesome. You can supply the image as Matplotlib Figure, Pillow Image or just as bytes if your image is in those formats. It uses Jinja for templating.

If you need to control the size of the image, you can also do this:

email.send(
sender="me@example.com",
receivers=["you@example.com"]
subject="An email with image",
html="""
<h1>Look at this:</h1>
<img src="\{\{ my_image.src }}" width=200 height=300>
""",
body_images={
"my_image": "path/to/image.png"
}
)

You can just pip install it:

pip install redmail

It's (hopefully) all you need for email sending (has a lot more) and it is well tested. I also wrote extensive documentation: https://red-mail.readthedocs.io/en/latest/ and source code is found here.

There's actually a really good post with multiple solutions to this problem here. I was able to send an email with HTML, an Excel attachment, and an embedded images because of it.