Getting mail attachment to python file object

Question:

I have got an email multipart message object, and I want to convert the attachment in that email message into python file object. Is this possible? If it is possible, what method or class in Python I should look into to do such task?

Asked By: Joshua Partogi

||

Answers:

I don’t really understand what you mean by “email multipart message object”. Do you mean an object belonging to the email.message.Message class?

If that is what you mean, it’s straightforward. On a multipart message, the get_payload method returns a list of message parts (each of which is itself a Message object). You can iterate over these parts and examine their properties: for example, the get_content_type method returns the part’s MIME type, and the get_filename method returns the part’s filename (if any is specified in the message). Then when you’ve found the correct message part, you can call get_payload(decode=True) to get the decoded contents.

>>> import email
>>> msg = email.message_from_file(open('message.txt'))
>>> len(msg.get_payload())
2
>>> attachment = msg.get_payload()[1]
>>> attachment.get_content_type()
'image/png'
>>> open('attachment.png', 'wb').write(attachment.get_payload(decode=True))

If you’re programmatically extracting attachments from email messages you have received, you might want to take precautions against viruses and trojans. In particular, you probably ought only to extract attachments whose MIME types you know are safe, and you probably want to pick your own filename, or at least sanitize the output of get_filename.

Answered By: Gareth Rees

Here is working solution, messages are form IMAP server

self.imap.select()
typ, data = self.imap.uid('SEARCH', 'ALL')
msgs = data[0].split()
print "Found {0} msgs".format(len(msgs))

for uid in msgs:
    typ, s = self.imap.uid('FETCH', uid, '(RFC822)')
    mail = email.message_from_string(s[0][1])

    print "From: {0}, Subject: {1}, Date: {2}n".format(mail["From"], mail["Subject"], mail["Date"])

    if mail.is_multipart():
        print 'multipart'
        for part in mail.walk():
            ctype = part.get_content_type()
            if ctype in ['image/jpeg', 'image/png']:
                open(part.get_filename(), 'wb').write(part.get_payload(decode=True))
Answered By: pma_

Actually using now-suggested email.EmailMessage API (don’t confuse with old email.Message API) it is fairly easy to:

  1. Iterate over all message elements and select only attachments

  2. Iterate over just attachments

Let’s assume that you have your message stored as byte content in envelope variable

Solution no.1:

import email
from email.message import EmailMessage

email_message: EmailMessage = email.message_from_bytes(envelope, _class=EmailMessage)

for email_message_part in email_message.walk():
    if email_message.is_attachment():
        # Do something with your attachment

Solution no.2: (preferable since you don’t have to walk through other parts of your message object)

import email
from email.message import EmailMessage

email_message: EmailMessage = email.message_from_bytes(envelope, _class=EmailMessage)

for email_message_attachment in email_message.iter_attachments():
        # Do something with your attachment

Couple things to note:

  1. We explicitly tell to use new EmailMessage class in our byte read method through _class=EmailMessage parameter
  2. You can read your email message (aka envelope) from sources such as bytes-like object, binary file object or string thanks to built-in methods in message.Parser API
Answered By: Jakub Pastuszuk
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.