5

I am trying to embed an image in an email. I have followed examples here, here and here and others however I cannot get the image to display.

    import smtplib
    import os

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

    logo = 'mylogo.png'
    msg = MIMEMultipart('alternative')
    msg['Subject'] = "Link"
    msg['From'] = 'sender@email.com'
    msg['To'] = 'recipient@email.com'

    html = """\
    <html>
      <head></head>
    <body>
      <p>GREETING<br><br>
       SOME TEXT<br>
       MORE TEXT<br><br>
       FAREWELL <br><br>
       DISCLAIMER
    </p>
    <img src="cid:image1" alt="Logo" \>
    </body>
    </html> """

    part1 = MIMEText(text, 'plain')
    part2 = MIMEText(html, 'html', 'utf-8')

    msg.attach(part1)
    msg.attach(part2)

    fp = open(logo, 'rb')
    msgImage = MIMEImage(fp.read())
    fp.close()

    msgImage.add_header('Content-ID', '<image1>')
    msgImage.add_header('Content-Disposition', 'inline', filename=os.path.basename(logo))
    msgImage.add_header("Content-Transfer-Encoding", "base64")
    msg.attach(msgImage)

    s = smtplib.SMTP(smtp_server,25)
    s.sendmail(sender, recipient, msg.as_string())
    s.quit()

When I execute this, I get an empty body with a red cross in it and no image. How do I get the image to be displayed inline with the email body?

I am using Outlook 2016. I know I can insert pictures when using Outlook itself and I have received 'normal' emails where others have inserted images within the text so surely this means I must be able to view images generated from a python script?

EDIT: I have looked at the solution given here, suggested as a possible duplicate, but this has not solved my problem either.

I have also tried sending the same email to a Gmail and a hotmail account and the same problem still arises so the problem is clearly something to do with code.

  • Possible duplicate of [Embed picture in email](https://stackoverflow.com/questions/7755501/embed-picture-in-email) – Syfer Aug 05 '19 at 01:23

2 Answers2

3

I found a solution for this in Outlook 2013:

Outlook seems to look for span tags as well as some of the image info like the height and width (hardcoded for now) and the id etc. (see below) I wrapped my image in this html text and then also added the image as an attachment using the same number i used for the cid and id. images then show up as embedded files in Outlook.

    # loop through all uploaded files and add as attachments
    for index, file in enumerate(attachment):
       with open(file, "rb") as fp:
          data = fp.read()
          fileType = os.path.splitext(file)[-1].strip(".")

          ctype, _ = mimetypes.guess_type(file)

          if embedImages and "image/" in ctype:
             image = Image.open(data)
             # Build out the html for the email
             message += '<span style = "mso-no-proof:yes"><img width = 1280 height = 1920 id = "%s" src = "cid:%s"></span>' % (index, index)
             # Define the image's ID
             msgImage = MIMEImage(data, _subtype=fileType)
             msgImage.add_header('Content-ID', '<%s>' % index)
             msg.attach(msgImage)
             att = MIMEApplication(data, _subtype=fileType)
             att.add_header("Content-Disposition", "attachment", filename="%s" % index)
             msg.attach(att)
        else:
             att = MIMEApplication(data, _subtype=fileType)
             att.add_header("Content-Disposition", "attachment", filename=fileName)
             msg.attach(att)
        fp.close()

        # Attach html text
        if message:
            if embedImages or "mimeType" in kwargs:
                msg.attach(MIMEText(message, kwargs["mimeType"], 'utf-8'))
            else:
                msg.attach(MIMEText(message, "plain", 'utf-8'))
Debbie S
  • 61
  • 3
3

Here is another solution which uses the MIMEMultipart('related') piece so you don't have to deal with the spans in Outlook. Works for multiple attachments of any type. (Images will be embedded unless specified otherwise)

    # Determine attach type - defaults to attached only
    embedImages = kwargs["embedImages"] if "embedImages" in kwargs else False

    # Create the root message and fill in the from, and subject headers
    msgRoot = MIMEMultipart('related')
    msgRoot["Subject"] = subject
    msgRoot["From"] = kwargs["fromAddr"] if "fromAddr" in kwargs else noreply

    # 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)

    # Get Recipient Type
    recipientType = kwargs.get("recipientType")

    # Chunk Process Recipients
    batchSize = 100
    for i in range(0, len(recipientList), batchSize):
        # Handle 100 recipients at a time
        recipients = recipientList[i:i + batchSize]
        # Set the to, bcc or cc in root message
        if recipientType == "bcc":
            msgRoot["Bcc"] = ", ".join(recipients)
        elif recipientType == "cc":
            msgRoot["Cc"] = ", ".join(recipients)
        else:
            msgRoot["To"] = ", ".join(recipients)

        # Add Attachments
        if attachment and isinstance(attachment, str):
            # For backwards compatibility turn single attachment into a list
            attachment = [attachment]

        # loop through all uploaded files and add as attachments
        for index, file in enumerate(attachment):
            with open(file, "rb") as fp:
                # guess the file type from mimetypes
                ctype, _ = mimetypes.guess_type(file)
                if embedImages and "image/" in ctype:
                    # Build out the html for the email
                    message += '<p><img src="cid:%s"></p>' % index
                    msgImage = MIMEImage(fp.read())
                    # Define the image's ID in header
                    msgImage.add_header('Content-ID', '<%s>' % index)
                    # attach it to root
                    msgRoot.attach(msgImage)
                else:
                    fileName = alias if alias and len(attachment) == 1 else os.path.basename(file)
                    msgApp = MIMEApplication(fp.read())
                    # define header as attachment
                    msgApp.add_header("Content-Disposition", "attachment", filename=fileName)
                    # attach it to root
                    msgRoot.attach(msgApp)
                # close the file handle
                fp.close()

        # Attach html text
        if message:
            if embedImages or "mimeType" in kwargs:
                msgAlternative.attach(MIMEText(message, kwargs["mimeType"], 'utf-8'))
            else:
                msgAlternative.attach(MIMEText(message, "plain", 'utf-8'))
Debbie S
  • 61
  • 3