Search code examples
pythonhtmlemailmimegmail-api

Base64 encoded image in email


I am trying to send html e-mails with base64 encoded images embeded in them, using gmail api and python.

This is the html file that I want to send as email

<h1>This is a test message</h1>
<p>
<img src="data:image/png;base64,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">
</p>

Here are my codes

"""Send an email message from the user's account.
"""

import base64
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import mimetypes
import os

from apiclient import errors



def send_message(service, user_id, message):
  """Send an email message.

  Args:
    service: Authorized Gmail API service instance.
    user_id: User's email address. The special value "me"
    can be used to indicate the authenticated user.
    message: Message to be sent.

  Returns:
    Sent Message.
  """
  try:
    message = (service.users().messages().send(userId=user_id, body=message)
               .execute())
    print 'Message Id: %s' % message['id']
    return message
  except errors.HttpError, error:
    print 'An error occurred: %s' % error


def create_message(sender, to, subject, message_text):
  """Create a message for an email.

  Args:
    sender: Email address of the sender.
    to: Email address of the receiver.
    subject: The subject of the email message.
    message_text: The text of the email message.

  Returns:
    An object containing a base64url encoded email object.
  """
  message = MIMEText(message_text,'html')
  message['to'] = to
  message['from'] = sender
  message['subject'] = subject
  return {'raw': base64.urlsafe_b64encode(message.as_string())}

if __name__ == "__main__":
    from login import service

    with open("test.html","rb") as f:
        message_body = f.read()

    m = create_message("xxx@gmail.com","xxx@gmail.com","test message",message_body)

    send_message(service, "me", m)

However, I don't see my image in the e-mail content. When I inspect the original, I can see that my html is malformed like this;

Content-Type: multipart/alternative; boundary=001a114442124f125f053789c367

--001a114442124f125f053789c367
Content-Type: text/plain; charset=UTF-8

This is a test message

--001a114442124f125f053789c367
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

<h1>This is a test message</h1>
    <p>
    <img src=3D"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAMAA=
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3D">
    </p>

--001a114442124f125f053789c367--

As you can see, there is a 3D inserted in <img src=3D"data:image/png;base64 ... part.

What is the correct way to embed images in e-mails using gmail api?

Addition

After further investigation, I have realized that if I convert =3D in my e-mail back to =, image is still broken.

I think problem might be related to the fact that my base64 encoded data is splitted into multiple lines in e-mail source code.


Solution

  • Instead of using embed base64 encoding, I decided to add images as attachments. Here is my new test.html file;

    <h1>This is a test message</h1>
        <p>
        <img src="cid:asdfgh">
        </p>
    

    And here is my code

    """Send an email message from the user's account.
    """
    
    import base64
    from email.mime.audio import MIMEAudio
    from email.mime.base import MIMEBase
    from email.mime.image import MIMEImage
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    import mimetypes
    import os
    
    from apiclient import errors
    
    
    def send_message(service, user_id, message):
      """Send an email message.
    
      Args:
        service: Authorized Gmail API service instance.
        user_id: User's email address. The special value "me"
        can be used to indicate the authenticated user.
        message: Message to be sent.
    
      Returns:
        Sent Message.
      """
      try:
        message = (service.users().messages().send(userId=user_id, body=message)
                   .execute())
        print 'Message Id: %s' % message['id']
        return message
      except errors.HttpError, error:
        print 'An error occurred: %s' % error
    
    
    def create_message(sender, to, subject, message_text):
      """Create a message for an email.
    
      Args:
        sender: Email address of the sender.
        to: Email address of the receiver.
        subject: The subject of the email message.
        message_text: The text of the email message.
    
      Returns:
        An object containing a base64url encoded email object.
      """
      message = MIMEText(message_text,'html')
      message['to'] = to
      message['from'] = sender
      message['subject'] = subject
      return {'raw': base64.urlsafe_b64encode(message.as_string())}
    
    def create_multipart_message(sender, to, subject, message_text):
    
          message = MIMEMultipart()
          message['to'] = to
          message['from'] = sender
          message['subject'] = subject
    
          msg = MIMEText(message_text,'html')
          message.attach(msg)
    
          return message
    
    def attach_file_to_multipart_message(message, file, content_id=None):
    
      content_type, encoding = mimetypes.guess_type(file)
    
      if content_type is None or encoding is not None:
        content_type = 'application/octet-stream'
    
      main_type, sub_type = content_type.split('/', 1)
      if main_type == 'text':
        fp = open(file, 'rb')
        msg = MIMEText(fp.read(), _subtype=sub_type)
        fp.close()
      elif main_type == 'image':
        fp = open(file, 'rb')
        msg = MIMEImage(fp.read(), _subtype=sub_type)
        fp.close()
      elif main_type == 'audio':
        fp = open(file, 'rb')
        msg = MIMEAudio(fp.read(), _subtype=sub_type)
        fp.close()
      else:
        fp = open(file, 'rb')
        msg = MIMEBase(main_type, sub_type)
        msg.set_payload(fp.read())
        fp.close()
      filename = os.path.basename(file)
      msg.add_header('Content-Disposition', 'attachment', filename=filename)
      if content_id:
        msg.add_header('Content-ID', '<%s>' % content_id)
    
      message.attach(msg)
    
    def finalize_message(message):
        return {"raw": base64.urlsafe_b64encode(message.as_string())}
    
    if __name__ == "__main__":
        from login import service
    
        m = create_multipart_message("xxxx@gmail","xxxx@gmail.com","test message", open("test.html").read())
        attach_file_to_multipart_message(m, "placeholder.png", "asdfgh")
    
        m_final = finalize_message(m)
    
        send_message(service, "me", m_final)
    

    Using this method, I was able to show images in my html e-mails.