I am currently trying to send a pdf attachment via Gmail API, but the file that I receive seems to be corrupted. Here is the code that I use to create the Email.
message_out = MIMEMultipart()
content_type, encoding = mimetypes.guess_type(attachment)
if content_type is None or encoding is not None:
content_type = 'application/octet-stream'
main_type, sub_type = content_type.split('/', 1)
with open(attachment, 'rb') as fp:
msg = MIMEBase(main_type, sub_type)
msg.set_payload(fp.read())
filename = os.path.basename(attachment)
msg.add_header('Content-Disposition', 'attachment', filename=filename)
msg.add_header('Content-Type', main_type, name=filename)
msg.add_header('Content-Transfer-Encoding', '7bit')
email.encoders.encode_base64(msg)
message_out.attach(msg)
return {'raw': base64.urlsafe_b64encode(message_out.as_bytes()).decode()}
When I try to open the attachment, I then get a "failed to load PDF document". I guess it has something to do with the encoding, but I cannot figure out why, I was thinking that email.encoders was going to solve all my problems. (same problem happens with png image)
Thank you very much,
Djazouli
As expected, this was an encoding issue, the line
msg.add_header('Content-Transfer-Encoding', '7bit')
shouldn't be there
Related
I am struggling with sending email with the attachment of excel file by Python.
The problem I am facing now is python change excel file format to '.aaf' format or other unknown formats.
It happens when python sends excel files to specific email addresses, which I guess are intranet mail address. Other personal email addresses(e.g. gmail) have no trouble, no change in file format. The assuming-intranet mail addresses also receieve pdf file with awkward format as well as excel file.
I am digging out any clue to solve it day and night, but can't find any advice related to it. Any help? Below is my code :
note) attachment = file path + file name
def send_mail(mail_list, cc_list, subject, text, attachment):
msg = EmailMessage()
msg["From"] = MY_ID
msg["To"] = ",".join(mail_list)
msg["Cc"] = ",".join(cc_list)
msg["Subject"] = subject
msg.set_content(text)
if attachment:
filename = Path(attachment).name
with open(filename, "rb") as f:
msg.add_attachment(f.read(), maintype="application", subtype="xlsx", filename=filename)
msg.add_header('Content-Disposition', 'attachment; filename='+filename)
with SMTP_SSL("smtp.gmail.com", 465) as smtp:
smtp.login(MY_ID, MY_PW)
smtp.send_message(msg)
A guy left a comment, which seems removed, and it solved the problem. The solution is :
replace the code I wrote
msg.add_attachment(f.read(), maintype="application", subtype="xlsx", filename=filename)
with one below :
msg.add_attachment(f.read(), maintype="application", subtype="vnd.openxmlformats-officedocument.spreadsheetml.sheet", filename=filename)
I want to write a function to attach a csv file to an E-Mail with HTML text. Everything works fine. I sends the E-Mail with the text and the attachment with the right information. Only the data format is wrong. I tried different varieties in MIMEBASE('application', 'octet-stream'). This one gives me a '.bin' data which I cannot open on macOS. Others gave me a plain text data which included the right data, but I don't wanna copy it manually into a csv. Does someone have a solution how I get out the data as '.csv'? Most of the solutions I found here just looked like my code below.
Code:
def send_mail(receiver, subject, filepath, attachname):
#login information
port = 123
smtp_server = "test.server.com"
login = "testlogin" # your login
password = "12345678910" # your password
# specify the sender’s and receiver’s email addresses and text in HTML
sender = "testmail#test.com"
receiver = receiver
message = MIMEMultipart()
message["Subject"] = subject
message["From"] = sender
message["To"] = COMMASPACE.join([receiver])
html_text = """\
<html>
<body>
<p>Hi ,<br>
<p>kind regards</p>
<p> This is an automatized Mail </p>
</body>
</html>
"""
# convert both parts to MIMEText objects and add them to the MIMEMultipart message
text = MIMEText(html_text, "html")
message.attach(text)
#add a file as attachment
file = open(filepath, "rb")
att = MIMEBase('application', 'octet-stream')
att.set_payload(file.read())
encoders.encode_base64(att)
att.add_header("Content_Disposition",
'attachment', filename = attachname)
message.attach(att)
try:
#send your message with credentials specified above
with smtplib.SMTP(smtp_server, port) as server:
server.connect(smtp_server, port)
server.ehlo()
server.starttls()
server.ehlo()
server.login(login, password)
server.sendmail(sender, receiver, message.as_string())
# tell the script to report if your message was sent or which errors need to be fixed
print('Sent')
except (gaierror, ConnectionRefusedError):
print('Failed to connect to the server. Bad connection settings?')
except smtplib.SMTPServerDisconnected:
print('Failed to connect to the server. Wrong user/password?')
except smtplib.SMTPException as e:
print('SMTP error occurred: ' + str(e))
send_mail('testmail#test.com', 'TEST', '/Users/Changer/Desktop/test.csv', 'test.csv')
Email clients may use the content type of the attachment part to determine which program is used to open the attachment, so specifying a suitable content type may help.
This code uses the standard library's mimetypes module to try to guess the correct content type of the attachment, based on its name:
import mimetypes
mimetypes.init()
def send_mail(receiver, subject, filepath, attachname):
...
mimetype, _ = mimetypes.guess_type(attachname)
if mimetype is None:
mimetype = 'application/octet-stream'
type_, _, subtype = mimetype.partition('/')
att = MIMEBase(type_, subtype)
...
The above code will generate these headers for the attachment:
b'Content-Type: text/x-comma-separated-values'
b'MIME-Version: 1.0'
b'Content-Transfer-Encoding: base64'
b'Content_Disposition: attachment; filename="test.csv"'
Whether a mac will open a csv with a spreadsheet application (I assume this is what you want) depends on the configuration of the machine (See this blog for an example). If you want to be sure open the file in a spreadsheet it might be more effective to convert it to a spreadsheet format and send the spreadsheet.
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)
I was trying to get a python program send an attachment via Gmail.
I used the sample code found:
Sending email via gmail & python
The problem is when I sent Excel files like .xls, .xlsx, .xlsm to myself, I cannot open the attachments as they were corrupted, even though the original files are fine. But sending .csv works fine. The entire process does not pop up any warnings or error.
Question is: did oauth2.0 or Gmail or MIME mess up the attachment for some formats? And how can I tell the program upload and send attachment without modifying it?
Had similar issue.
The error is probably in encoding file into bytes.
My gmail still sends corrupted file, when I do want to send xlsx, but I managed to get correct email with xls format.
This is my code:
def create_message_with_excel_attachment(sender, to, subject, message_text, file):
message = MIMEMultipart()
message['to'] = to
message['from'] = sender
message['subject'] = subject
msg = MIMEText(message_text)
message.attach(msg)
part = MIMEBase('application', "vnd.ms-excel")
part.set_payload(open(file, "rb").read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment', filename=file)
message.attach(part)
raw = base64.urlsafe_b64encode(message.as_bytes())
raw = raw.decode()
return {'raw': raw}
def send_message(service, user_id, message):
message = (service.users().messages().send(userId=user_id, body=message).execute())
try:
print ('Message Id: %s' % message['id'])
return message
except:
print ('An error occurred:')
I'm trying to attach a PDF file to an email like this:
def send_email(gmail, password, post_description,
reply_email, attachment_path, email_body_path):
msg = MIMEMultipart()
with open(email_body_path) as f:
msg.attach(MIMEText(f.read()))
if attachment_path != None:
with open(attachment_path, 'rb') as f:
msg.attach(MIMEApplication(
f.read(),
Content_Disposition='attachment, filename="%s"' % basename(f)))
smtp = smtplib.SMTP('smtp.gmail.com',587)
smtp.login(gmail, password)
smtp.sendmail(gmail, 'address#gmail.com', msg.as_string()
The PDF is attached and has the correct title, but the contents are always "ECO" and nothing else.
See email examples on how to make attachments correctly:
outer = MIMEMultipart()
<...>
fp = open(path, 'rb')
msg = MIMEAplication(fp.read(), _subtype=subtype) #or another appropriate subclass
#some subclasses have additional parameters
<or>
msg = MIMEBase(maintype, subtype)
msg.set_payload(fp.read())
encoders.encode_base64(msg)
msg.add_header('Content-Disposition', 'attachment', filename=filename)
outer.attach(msg)
To add text to the message, you need to attach a MIMEText as well as the PDF as per multipart/alternative example (do not forget to specify encoding if it's not the RFC-dictated default, iso-8859-1):
part = MIMEText(<text>, _charset='<charset>')
outer.attach(part)
So, you were doing everything correctly except forming the MIMEApplication. MIMEBase.__init__ source code shows that extra parameters on the constructor call all go into the Content-Type header.