Downloading multiple attachments using imaplib
For any future python travellers.Here is a class that downloads any attachment found for an email and saves it to a specific location.
import emailimport imaplibimport osclass FetchEmail(): connection = None error = None def __init__(self, mail_server, username, password): self.connection = imaplib.IMAP4_SSL(mail_server) self.connection.login(username, password) self.connection.select(readonly=False) # so we can mark mails as read def close_connection(self): """ Close the connection to the IMAP server """ self.connection.close() def save_attachment(self, msg, download_folder="/tmp"): """ Given a message, save its attachments to the specified download folder (default is /tmp) return: file path to attachment """ att_path = "No attachment found." for part in msg.walk(): if part.get_content_maintype() == 'multipart': continue if part.get('Content-Disposition') is None: continue filename = part.get_filename() att_path = os.path.join(download_folder, filename) if not os.path.isfile(att_path): fp = open(att_path, 'wb') fp.write(part.get_payload(decode=True)) fp.close() return att_path def fetch_unread_messages(self): """ Retrieve unread messages """ emails = [] (result, messages) = self.connection.search(None, 'UnSeen') if result == "OK": for message in messages[0].split(' '): try: ret, data = self.connection.fetch(message,'(RFC822)') except: print "No new emails to read." self.close_connection() exit() msg = email.message_from_bytes(data[0][1]) if isinstance(msg, str) == False: emails.append(msg) response, data = self.connection.store(message, '+FLAGS','\\Seen') return emails self.error = "Failed to retreive emails." return emails def parse_email_address(self, email_address): """ Helper function to parse out the email address from the message return: tuple (name, address). Eg. ('John Doe', 'jdoe@example.com') """ return email.utils.parseaddr(email_address)
I reworked the code, breaking it up into functions. I use PEEK
so I don't change the UNREAD status of the email messages.
I'm posting my take on the problem, similar to @John, but I use only functions instead of classes:
import imaplibimport email# Connect to an IMAP serverdef connect(server, user, password): m = imaplib.IMAP4_SSL(server) m.login(user, password) m.select() return m# Download all attachment files for a given emaildef downloaAttachmentsInEmail(m, emailid, outputdir): resp, data = m.fetch(emailid, "(BODY.PEEK[])") email_body = data[0][1] mail = email.message_from_string(email_body) if mail.get_content_maintype() != 'multipart': return for part in mail.walk(): if part.get_content_maintype() != 'multipart' and part.get('Content-Disposition') is not None: open(outputdir + '/' + part.get_filename(), 'wb').write(part.get_payload(decode=True))# Download all the attachment files for all emails in the inbox.def downloadAllAttachmentsInInbox(server, user, password, outputdir): m = connect(server, user, password) resp, items = m.search(None, "(ALL)") items = items[0].split() for emailid in items: downloaAttachmentsInEmail(m, emailid, outputdir)
You code appears okay except for the return
(perhaps a typo?) right after the fp.close()
:
...fp.write(part.get_payload(decode=True))fp.close()return HttpResponse('check folder')
After saving the first attachment it returns from the function. Comment out that line and see if it fixes your issue.