How to get email attachment size in python imap
# DOWNLOAD ATTACHMENTS
for part in msg.walk():
# this part comes from the snipped I don't understand yet...
if part.get_content_maintype() == 'multipart':
continue
if part.get('Content-Disposition') is None:
continue
fileName = part.get_filename()
return HttpResponse(fileSize)
if bool(fileName):
filePath = os.path.join('C:/Users/bali/attachments/', fileName)
if not os.path.isfile(filePath) :
fp = open(filePath, 'wb')
fp.write(part.get_payload(decode=True))
fp.close()
Is there any function to get size of the attachment just like "get_filename()" to get the name of the file.
Information about the attachment size is not available in the header of the MIME message (you can check this by sending an attachment to yourself and seeing the original email to see if there is any information about attachment size) but you can get the size of the attachment without "creating a file", which I consider is equivalent to "downloading the attachment".
You can do so by getting the payload of the part with attachment and then returning the length of the payload:
payload = part.get_payload(decode=True)
file_size = len(payload) # in bytes
Also, instead of checking for part.get_filename(), as you did in your sample code, I recommend using is_attachment() check on the message part OR instead of walk() using iter_attachments() to get all the message parts with attachments. You can see how the attachments are handled in this python emails document examples.
Well, you have the filename, so you can use os.path.getsize
import os
os.path.getsize(part.get_filename())
You may try external lib: https://github.com/ikvk/imap_tools
from imap_tools import MailBox, A
with MailBox('imap.mail.com').login('test#mail.com', 'pwd', 'INBOX') as mailbox:
for msg in mailbox.fetch(A(all=True)):
print(msg.subject, msg.date)
for att in msg.attachments:
print(att.filename, att.size)
Related
I am trying to fetch all the attachments of email messages and make a list of those attachments for that particular mail and save that list in a JSON file.
I have been instructed to use imaplib only.
This is the function that I am using to extract the mails data but the part.getfilename() is only returning one attachment even if I have sent multiple attachments.
The output I want is the list of attachments like [attach1.xlss, attach2.xml, attch.csv].
Again, I can only use imaplib library.
I also don't want to have to download any attachment, so please don't share that code. I tried several websites but couldn't find anything that I could use.
def get_body_and_attachments(msg):
email_body = None
filename = None
html_part = None
# if the email message is multipart
if msg.is_multipart():
# iterate over email parts
for part in msg.walk():
# extract content type of email
content_type = part.get_content_type()
content_disposition = str(part.get("Content-Disposition"))
try:
# get the email body
body = part.get_payload(decode=True).decode()
except:
pass
if content_type == "text/plain" and "attachment" not in content_disposition:
# print text/plain emails and skip attachments
email_body = body
elif "attachment" in content_disposition:
# download attachment
print(part.get_filename(), "helloooo")
filename = part.get_filename()
filename = filename
else:
# extract content type of email
content_type = msg.get_content_type()
# get the email body
body = msg.get_payload(decode=True).decode()
if content_type == "text/plain":
email_body = body
if content_type == "text/html":
html_part = body
return email_body, filename, html_part
It was easy; I just had to do this.
import re
# getting filenames
filenames = mailbox.uid('fetch', num, '(BODYSTRUCTURE)')[1][0]
filenames = re.findall('\("name".*?\)', str(filenames))
filenames = [filenames[i].split('" "')[1][:-2] for i in range(len(filenames))]
Explanation: mailbox.uid will fetch the message (or mail) of a particular uid (num) and will return a byte string with all the data relating to that message.
Now I use re.findall to find all the attachment names and then I clean that return value and save it as a list.
I am trying to send an email using Python smtplib.
My objective is to include the below info in email
Attachment file #works fine
Paste the contents of a table in message body #works fine
Write a few lines about the table (as text) in message body # not works. instead stores as an attachment
So, I tried the below code
message = MIMEMultipart()
message['Subject'] = 'For your review - files'
message['From'] = 'user2#org.com'
message['To'] = 'user1#org.com'
# code to paste table contents in outlook message window - works fine
body_content = output # this has the pretty table - html table
message.attach(MIMEText(body_content, "html"))
# code to paste the written text in outlook message window - not works. instead of writing the text in outlook body,it stores as an attachment
written_text = """\
Hi,
How are you?"""
message.attach(MIMEText(written_text, "plain"))
# code to attach an csv file to a outlook email - works fine
with open(filename, "rb") as attachment:
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
"Content-Disposition",
f"attachment; filename= {filename}",
)
message.attach(part)
msg_body = message.as_string()
server = SMTP('internal.org.com', 2089)
server.sendmail(message['From'], message['To'], msg_body)
print("mail sent successfully")
server.quit()
The problem in my code is that it creates a text file (containing the message "Hi, How are you") and sends as an attachment?
But I want "Hi, How are you" as a text message in the main Outlook message window.
The immediate problem is that many email clients assume that text body parts after the first are attachments. You can experiment with adding an explicit Content-Disposition: inline to the part(s) you want rendered as part of the main message, but is there a reason these need to be separate body parts in the first place? Combining the text fragments into a single body part would perhaps make more sense here.
More fundamentally, your email code was written for an older Python version. The email module in the standard library was overhauled in Python 3.6 to be more logical, versatile, and succinct; new code should target the (no longer very) new EmailMessage API. Probably throw away this code and start over with modern code from the Python email examples documentation.
from email.message import EmailMessage
message = EmailMessage()
message['Subject'] = 'For your review - files'
message['From'] = 'user2#org.com'
message['To'] = 'user1#org.com'
message.set_content(output, subtype="html")
written_text = """\
Hi,
How are you?"""
message.add_attachment(
written_text, subtype="plain",
disposition="inline")
with open(filename, "rb") as attachment:
message.add_attachment(
attachment.read(),
maintype="application", subtype="octet-stream",
filename=filename)
with SMTP('internal.org.com', 2089) as server:
server.send_message(message)
print("mail sent successfully")
server.quit()
If the final attachment is really a CSV file, specifying it as application/octet-stream is a bit misleading; the proper MIME type would be text/csv (see also What MIME type should I use for CSV?)
I am using IMAP library in python to read an email inbox which is working file and i am downloading all my attachment successfully but when any .eml file is coming as attachment i got an error, pease help me how to download an eml file coming as attachment.
In-order to download an attachment such as .png from an email, the payload needs to be decoded using: part.get_payload(decode=True).decode(). However, from the documentation:
If the message is a multipart and the decode flag is True, then None is returned.
The error you are seeing is caused because a .eml file is a multipart message. The parts consist of message/rfc822 at the top level which holds all the email's details. Beneath will be single part messages such as text/html which holds the email's text etc...
To download this text into an .html or .txt file you need to .walk() through the parts of the .eml file - like you are doing on the original email to download the .eml attachment.
Here is a snippet of my code:
if msg.is_multipart():
for part in msg.walk():
# extract content type of email
content_type = part.get_content_type()
content_disposition = str(part.get("Content-Disposition"))
if "attachment" in content_disposition:
if content_type == "message/rfc822":
# walk through the .eml attachment parts:
for eml_part in part.walk():
# find the content type of each part:
content_type = eml_part.get_content_type()
if content_type == "text/html": # this type is not multipart
body = eml_part.get_payload(decode=True).decode() # get_payload() can be decoded
# can do what you need with the decoded body.
# in this case extract text and save to .txt or .html
else: .....
Maybe you need to use EML Parser?
You can find the manual for eml-parser here.
You can use it:
def _read(self):
"""Reads all emails and get attachments.
Returns:
Attachments.
"""
self.mail.list()
self.mail.select(self.select)
self.mail.uid('search', None, 'ALL')
self.uids = self.data[0].split()
self.content_length = len(self.uids)
self.attachments = []
for uid in self.uids:
self.result, self.email_data = self.mail.uid(
'fetch', uid, '(RFC822)')
self.raw_email = self.email_data[0][1]
self.raw_email_string = self.raw_email.decode('utf-8')
self.parsed_email = email.message_from_bytes(self.raw_email)
for part in self.parsed_email.walk():
if part.get_content_maintype() == 'multipart':
continue
if part.get_content_type() not in ['text/html', 'text/plain']:
self.attachments.append({
'name':
part.get_filename(),
'content_type':
part.get_content_type(),
'bytes':
part.get_payload(decode=True)
})
self.result = {'attachments': self.attachments}
return self.result
Try to use my high level imap lib:
https://github.com/ikvk/imap_tools
from imap_tools import MailBox, MailMessage
# get .eml files attached to email messages from INBOX
with MailBox('imap.mail.com').login('test#mail.com', 'password', 'INBOX') as mailbox:
for message in mailbox.fetch():
for att in message.attachments:
if '.eml' in att.filename:
print(att.filename, len(att.payload))
Also you can parse .eml in place - see lib examples:
https://github.com/ikvk/imap_tools/blob/master/examples/parse_eml_attachments.py
I am trying to connect to outlook using python and read emails and write them to an output file along with all the corresponding attachments.
This is what I have so far:
import win32com.client
import unicodecsv as csv
import os
output_file = open('./outlook_farming_001.csv','wb')
output_writer = csv.writer(output_file, delimiter=';', encoding='latin2')
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6).Folders.Item("Security Availabilities")
messages = inbox.Items
for i, message in enumerate(messages):
try:
sender = message.SenderName
sender_address = message.SenderEmailAddress
sent_to = message.To
date = message.LastModificationTime
subject = message.subject
body = message.body
attachments = message.Attachments
attachment = attachments.Item(1)
for attachment in message.Attachments:
attachment.SaveAsFile(os.path.join(output_file, str(attachment)))
output_writer.writerow([
sender,
sender_address,
subject,
body,
attachment])
except Exception as e:
()
output_file.close()
Without the attachment stuff in the code- it works fine. I am able to read all the emails from my specific subfolder.
However, I am unable to read, save and display attachments along with their corresponding emails.
I think your mistake is in using str(attachment) in the filename - this will cause trouble because it should give some sort of '<COMObject <unknown>>' string.
Instead, use the following:
for attachment in message.Attachments:
attachment.SaveAsFile(os.path.join(output_file, attachment.FileName))
I hope this helps!
I am sending mail from a linux box from a directory path which has three csv files. I want to attach all three in my email. Below is the script.
def mailer(sender, to, path):
msg = MIMEMultipart()
msg['Subject'] = 'UMR_EOD_RECONCILLATIONS'
msg['From'] = sender
msg['To'] = to
for file in os.listdir(path):
f = open( path + file, 'rb')
csv = MIMEText(f.read())
f.close()
msg.attach(csv)
mailer = smtplib.SMTP('localhost')
mailer.sendmail(sender,to, msg.as_string())
mailer.quit()
I have been scratching my head for a while and tried multiple times but still facing below issues.
The files which are attached are text files i.e. .txt extension, I want is to be csv
The files have funny names ATT00001.txt and ATT00002.txt which remains the same.
The third file is never attached to the mail it contents are outputted in the body and it's the damn same file however times I may try.
I have tried setting below but to no avail.
msg["Content-Disposition"] = "attachment; filename=" + file + ";"
msg.add_header('Content-Disposition', 'attachment', filename=file)
1) The first text object will be displayed as the email message. So, add an extra text object first.
2) CSV files should be transmitted as content-type: text/csv, not content-type: text/plain.
#UNTESTED
def mailer(sender, to, path):
msg = MIMEMultipart()
msg['Subject'] = 'UMR_EOD_RECONCILLATIONS'
msg['From'] = sender
msg['To'] = to
msg.attach(MIMEText('Here are the reports you asked for.'))
for file in os.listdir(path):
f = open( path + file, 'rb')
csv = MIMEText(f.read(), 'csv')
f.close()
csv.add_header('Content-Disposition', 'attachment', filename=file)
msg.attach(csv)
mailer = smtplib.SMTP('localhost')
mailer.sendmail(sender,to, msg.as_string())
mailer.quit()
I always advise to run from doing MIME stuff when you just want to send emails. I feel like no one wants to deal with that. It feels like Java.
Try yagmail; my apologies, I'm the developer.
Its purpose is to make it super easy to send emails with HTML, inline images and attachments.
The code for what you want:
import os
import yagmail
def mailer(sender, to, path):
yag = yagmail.SMTP(sender, host="localhost", smtp_skip_login=True)
contents = ['Here are the reports you asked for.'] + os.listdir(path)
yag.send(to, 'UMR_EOD_RECONCILLATIONS', contents)
I'd suggest to read the README for more nice tricks in there :)
To get started, use pip install yagmail to install yagmail.