Search code examples
pythonmimemailgun

Mailgun inline images not displaying on iOS mail app


I'm using Mailgun to send an email with inline images. The following correctly displays the images inline in a browser (and the iOS gmail app), but not on the iOS Mail app.

    data = {
        'o:dkim': 'yes',
        'to': <TO_EMAIL>,
        'from': <FROM_EMAIL>,
        'html': '<html><img src="cid:inline[0]"></html>',
        'subject': 'test',
    }

    buf = BytesIO()
    fig.savefig(buf, format="png")  # matplotlib chart
    buf.seek(0)

    url = "https://api.mailgun.net/v3/%s/messages" % MAILGUN_DOMAIN
    r = requests.post(url, data=data, files=[('inline[0]', buf)], auth=('api', <AUTH>))

On the iOS mail app, the image doesn't display (just shows a small square that pops up "Cannot Download Attachment" when I click on it).

Related posts suggest sending a "multipart/related" MIME message, but from inspecting the email in chrome, Mailgun does seem to be doing that part properly - looking at the original message I see:

Mime-Version: 1.0
Content-Type: multipart/related; boundary="edd20bf01a194c43906131936d0ba59e"

Can anyone see what I'm missing?


Solution

  • I found a workaround for this issue. I believe it's to do with the fact that iOS products require an RFC 5322 compliant Message-ID, as mentioned in the django-anymail codebase (which was part of my solution).

    Ultimately I probably could have just created a compliant CID, but I reworked my code to incorporate utility functions offered by anymail and django.core.mail. Will update if I get around to trying out the former line of investigation.

    Here's what I got working:

    from anymail.message import attach_inline_image
    from django.core.mail import EmailMultiAlternatives
    
    msg = EmailMultiAlternatives(
        from_email=<FROM_EMAIL>,
        to=<TO_EMAIL>,
        subject='test')
    
    buf = BytesIO()
    fig.savefig(buf, format="png")  # matplotlib chart
    buf.seek(0)
    buf_id = 0
    
    cid = attach_inline_image(msg, buf.read(), idstring=buf_id, filename=buf_id)
    
    html = '<html><img src="cid:{}"></html>'.format(cid) 
    
    msg.attach_alternative(html, "text/html")
    msg.send()