【问题标题】:Send e-mail to Gmail with inline image using Python使用 Python 向 Gmail 发送带有内联图像的电子邮件
【发布时间】:2013-10-10 21:24:52
【问题描述】:

我的目标是使用 Python 向具有内嵌图像的 Gmail 用户发送电子邮件。由于图像的敏感性(来自我工作的数据),无法在线托管此图像,然后通过href 链接到它。

我尝试将base64 版本编码为HTML,然后发送HTML,但众所周知这是行不通的。然后我注意到,在 Gmail 中,您可以将图像拖放到发送框中,它将在接收端内联显示。鉴于此,我尝试从 Python 发送一封电子邮件,并将图像作为附件。这在下面的代码中可以看到,但不幸的是图像没有内联显示。

那么我的问题是:如何发送图像以使其内联显示?

import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email import Encoders
import os

gmail_user = "user1@gmail.com"
gmail_pwd = "pass"

to = "user2@gmail.com"
subject = "Report"
text = "Picture report"
attach = 'TESTING.png'

msg = MIMEMultipart()

msg['From'] = gmail_user
msg['To'] = to
msg['Subject'] = subject

msg.attach(MIMEText(text))

part = MIMEBase('application', 'octet-stream')
part.set_payload(open(attach, 'rb').read())
Encoders.encode_base64(part)
part.add_header('Content-Disposition',
   'attachment; filename="%s"' % os.path.basename(attach))
msg.attach(part)

mailServer = smtplib.SMTP("smtp.gmail.com", 587)
mailServer.ehlo()
mailServer.starttls()
mailServer.ehlo()
mailServer.login(gmail_user, gmail_pwd)
mailServer.sendmail(gmail_user, to, msg.as_string())
# Should be mailServer.quit(), but that crashes...
mailServer.close()

当我手动将内联图像发送给自己时,“原始电子邮件”是这样的:

  Content-Type: multipart/related; boundary=047d7bd761fe73e03304e7e02237

--047d7bd761fe73e03304e7e02237
Content-Type: multipart/alternative; boundary=047d7bd761fe73e03004e7e02236

--047d7bd761fe73e03004e7e02236
Content-Type: text/plain; charset=ISO-8859-1

[image: Inline images 1]

--047d7bd761fe73e03004e7e02236
Content-Type: text/html; charset=ISO-8859-1

<div dir="ltr"><img alt="Inline images 1" src="cid:ii_141810ee4ae92ac6" height="400" width="534"><br></div>

--047d7bd761fe73e03004e7e02236--
--047d7bd761fe73e03304e7e02237
Content-Type: image/png; name="Testing.png"
Content-Transfer-Encoding: base64
Content-ID: <ii_141810ee4ae92ac6>
X-Attachment-Id: ii_141810ee4ae92ac6

当我通过 Python 将它作为附件发送给自己时,情况就大不相同了:

Content-Type: multipart/mixed; boundary="===============6881579935569047077=="
MIME-Version: 1.0
(.... some stuff deleted here)
--===============6881579935569047077==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit

See attachment for report.
--===============6881579935569047077==
Content-Type: application/octet-stream
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="TESTING.png"

【问题讨论】:

  • 我知道我迟到了,但我为这样的东西准备了一个简单的邮件类,因为我自己需要不时使用它datamakessense.com/…

标签: python image email gmail mime


【解决方案1】:

我认为您需要添加以下几行:

from email.mime.image import MIMEImage

body = MIMEText('<p>Test Image<img src="cid:testimage" /></p>', _subtype='html')
msg.attach(body)


img = MIMEImage(image.read(), 'jpeg')
img.add_header('Content-Id', '<testimage>')
msg.attach(img)

testimage 应替换为唯一标识符

【讨论】:

    【解决方案2】:

    似乎遵循 gmail 电子邮件模板有效:

    * multipart/alternative
      - text/plain
      - multipart/related
        + text/html
          <img src="cid:msgid"/>
        + image/png
          Content-ID: <msgid>
    

    基于email module docs的示例:

    #!/usr/bin/env python3
    import html
    import mimetypes
    from email.headerregistry import Address
    from email.message import EmailMessage
    from email.utils import make_msgid
    from pathlib import Path
    
    title = 'Picture report…'
    path = Path('TESTING.png')
    me = Address("Pepé Le Pew", *gmail_user.rsplit('@', 1))
    
    msg = EmailMessage()
    msg['Subject'] = 'Report…'
    msg['From'] = me
    msg['To'] = [me]
    msg.set_content('[image: {title}]'.format(title=title))  # text/plain
    cid = make_msgid()[1:-1]  # strip <>    
    msg.add_alternative(  # text/html
        '<img src="cid:{cid}" alt="{alt}"/>'
        .format(cid=cid, alt=html.escape(title, quote=True)),
        subtype='html')
    maintype, subtype = mimetypes.guess_type(str(path))[0].split('/', 1)
    msg.get_payload()[1].add_related(  # image/png
        path.read_bytes(), maintype, subtype, cid="<{cid}>".format(cid=cid))
    
    # save to disk a local copy of the message
    Path('outgoing.msg').write_bytes(bytes(msg))
    

    通过 gmail 发送msg

    import smtplib
    import ssl
    
    with smtplib.SMTP('smtp.gmail.com', timeout=10) as s:
        s.starttls(context=ssl.create_default_context())
        s.login(gmail_user, gmail_password)
        s.send_message(msg)
    

    Python 2/3 兼容版本

    * multipart/related
      - multipart/alternative
        + text/plain
        + text/html
          <div dir="ltr"><img src="cid:ii_xyz" alt="..."><br></div>
      - image/jpeg
        Content-ID: <ii_xyz>
    

    基于Send an HTML email with embedded image and plain text alternate:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import cgi
    import uuid
    import os
    from email.mime.multipart import MIMEMultipart
    from email.mime.text      import MIMEText
    from email.mime.image     import MIMEImage
    from email.header         import Header    
    
    img = dict(title=u'Picture report…', path=u'TESTING.png', cid=str(uuid.uuid4()))
    
    msg = MIMEMultipart('related')
    msg['Subject'] = Header(u'Report…', 'utf-8')
    msg['From'] = gmail_user
    msg['To'] = ", ".join([to])
    msg_alternative = MIMEMultipart('alternative')
    msg.attach(msg_alternative)
    msg_text = MIMEText(u'[image: {title}]'.format(**img), 'plain', 'utf-8')
    msg_alternative.attach(msg_text)
    
    msg_html = MIMEText(u'<div dir="ltr">'
                         '<img src="cid:{cid}" alt="{alt}"><br></div>'
                        .format(alt=cgi.escape(img['title'], quote=True), **img),
                        'html', 'utf-8')
    msg_alternative.attach(msg_html)
    
    with open(img['path'], 'rb') as file:
        msg_image = MIMEImage(file.read(), name=os.path.basename(img['path']))
        msg.attach(msg_image)
    msg_image.add_header('Content-ID', '<{}>'.format(img['cid']))
    

    通过 gmail 发送msg

    import ssl
    
    s = SMTP_SSL('smtp.gmail.com', timeout=10,
                 ssl_kwargs=dict(cert_reqs=ssl.CERT_REQUIRED,
                                 ssl_version=ssl.PROTOCOL_TLSv1,
                                 # http://curl.haxx.se/ca/cacert.pem
                                 ca_certs='cacert.pem')) 
    s.set_debuglevel(0)
    try:
        s.login(gmail_user, gmail_pwd)
        s.sendmail(msg['From'], [to], msg.as_string())
    finally:
        s.quit()
    

    SMTP_SSL 是可选的,您可以使用问题中的starttls 方法:

    import smtplib
    import socket
    import ssl
    import sys
    
    class SMTP_SSL(smtplib.SMTP_SSL):
        """Add support for additional ssl options."""
        def __init__(self, host, port=0, **kwargs):
            self.ssl_kwargs = kwargs.pop('ssl_kwargs', {})
            self.ssl_kwargs['keyfile'] = kwargs.pop('keyfile', None)
            self.ssl_kwargs['certfile'] = kwargs.pop('certfile', None)
            smtplib.SMTP_SSL.__init__(self, host, port, **kwargs)
    
        def _get_socket(self, host, port, timeout):
            if self.debuglevel > 0:
                print>>sys.stderr, 'connect:', (host, port)
            new_socket = socket.create_connection((host, port), timeout)
            new_socket = ssl.wrap_socket(new_socket, **self.ssl_kwargs)
            self.file = getattr(smtplib, 'SSLFakeFile', lambda x: None)(new_socket)
            return new_socket
    

    【讨论】:

    • 如何在与图片相同的电子邮件中添加文本?​​
    • 查看代码中的 MimeText 调用:它们在第一个参数中添加纯文本和 HTML 正文。你可以在那里写任何你喜欢的东西
    • (你错过了import os
    • @ABM:你看到顶部的# -*- coding: utf-8 -*-了吗? (代码在 Python 2 和 3 上都可以与 '…' 一起使用)
    • 使用 python3 版本的代码我收到了邮件但图像没有显示,我只是得到了备用。其他人有这个问题吗?
    猜你喜欢
    • 1970-01-01
    • 2017-02-23
    • 1970-01-01
    • 2011-11-22
    • 2014-12-24
    • 2020-07-12
    • 2015-07-29
    • 2010-11-08
    • 2018-11-08
    相关资源
    最近更新 更多