Sending xlsx file using SMTP & Python 3 - python

I'm having trouble getting the SMTP server to keep my filenames as I attach them to emails and send them. I ran this twice, and it worked perfectly. The name and excel sheet showed as they were supposed to. Now, no matter what I do, the attachment is always something like ATT00001.xlsx when it used to work just fine. (literally left for lunch break and re-ran it when I got back with no changes) I'm wondering if it's how i'm attaching the excel sheet to my email. Would anyone happen to know what's going on with this? Thanks!
msg = MIMEMultipart()
sender='email#email.org'
recipients='email#recipient.org'
server=smtplib.SMTP('mail.server.lan')
msg['Subject']='Quarterly Summary'
msg['From']=sender
msg['To']=recipients
filename = r'C:\Users\user.chad\Quarterly\project\output\MyData.xlsx'
attachment = open(r'C:\Users\user.chad\Quarterly\project\output\MyData.xlsx', 'rb')
xlsx = MIMEBase('application','vnd.openxmlformats-officedocument.spreadsheetml.sheet')
xlsx.set_payload(attachment.read())
encoders.encode_base64(xlsx)
xlsx.add_header('Content-Dispolsition', 'attachment', filename=filename)
msg.attach(xlsx)
server.sendmail(sender, recipients, msg.as_string())
server.quit()
attachment.close()

Just for the record:
xlsx.add_header('Content-Dispolsition', 'attachment', filename=filename)
should be
xlsx.add_header('Content-Disposition', 'attachment', filename=filename)

This is an example to add an attachment with text/html conent.
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
message = MIMEMultipart()
# add text
message.attach(
MIMEText(
content,
'html',
'utf-8'
)
)
# add attachment
attachment = MIMEBase('application', "octet-stream")
# open a file to attach
attachment.set_payload(open(filepath, "rb").read())
encoders.encode_base64(attachment)
attachment.add_header('Content-Disposition', 'attachment; filename="%s"' % self.filename )
message.attach(attachment)
server.sendmail(SENDER, receivers, message.as_string())

Related

Python: sending mail via python creates unknown attachment

I'm trying to send a mail + attachment (.pdf file) via python.
The mail is send but the attachment becomes an unknown attachment instead of being a .pdf file
My code looks like this:
import smtplib
import os
import ssl
import email
from email import encoders
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
port = 465
smtp_server = "smtp.gmail.com"
subject = "An example of txt.file"
sender = "..."
receiver = "..."
password = "..."
message = MIMEMultipart()
message["From"] = sender
message["To"] = receiver
message["Subject"] = subject
filename = '318.pdf'
attachment = open(filename, "rb")
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
part.add_header('Content Disposition', 'attachment', filename=filename)
encoders.encode_base64(part)
message.attach(part)
message.attach(part)
body = "This is an example of how to send an email with an .pdf-attachment."
message.attach(MIMEText(body, 'plain'))
text = message.as_string()
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", port, context=context) as server:
server.login(sender, password)
server.sendmail(sender, receiver, text)
print('Sent')
What is wrong with it or what do I have to do differently?
I've tried different file types, the .pdf file is in the python file directory,...
You wrote
part.add_header('Content Disposition', 'attachment', filename=filename)
But the 'filename' argument-name must be provided as a string and you also missed the hyphen in 'Content-Disposition'. Try
part.add_header('Content-Disposition', 'attachment; filename=' + filename)
This should solve your issue. Just pointing out, you attached 'part' twice - on lines 20 and 22.
I think you might already be following this article. But if not, I think you'll find it useful.

Sending Email to different recipients with different file attachments using python

Hi I'm new to learning python and I'm trying to send emails to a few hundred different recipients with a different attachment to each of them. I have the recipients data in a database and a folder with all the files in it.
Database has supplier id, name and email
Database example
The folder structure is just one folder with all the files in it for example :
Folder screenshot
Supplier files folder --
123.xls
123.pdf
456.xls
789.pdf
any direction on how to get started with this is appreciated. Thank you
In this example I will use an answer that I gave to another question (How do I attach separate PDF's to contact list email addresses using Python?) , I use a csv file that contains the addresses and the file path that corresponds to it like img bellow :
users.csv :
folder with csv files :
And this is the code:
import smtplib, ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from string import Template
import pandas as pd
# read the file containing the database with the mail and the corresponding file
e = pd.read_csv("users.csv")
# In this example we will use gmail
context = ssl.create_default_context()
server = smtplib.SMTP_SSL('smtp.gmail.com', 465,context=context)
server.login('mymail#gmail.com','mypass')
body = ("""
Hi there
Test message
Thankyou
""")
subject = "Send emails with attachment"
fromaddr='mymail#gmail.com'
for index, row in e.iterrows():
print (row["Emails"]+row["csv"])
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['Subject'] = subject
msg.attach(MIMEText(body, 'plain'))
filename = row["csv"]
toaddr = row["Emails"]
attachment = open(row["csv"], "rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(part)
text = msg.as_string()
server.sendmail(fromaddr, toaddr, text)
print("Emails sent successfully")
server.quit()
if you use a gmail account, check that you have it permissions to send emails from other types of clients (https://support.google.com/accounts/answer/6010255)

as_string() method return an AttributeError

I try to send message from gmail with python 3.6 by this part of code:
import smtplib as smtp
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import os
SUBJECT = *subject message*
SOURCE = *directory*
TEXT = *some text in message*
msg = MIMEMultipart()
msg['From'] = *send from email*
msg['To'] = *send to email*
msg['Subject'] = SOURCE
#################### part with attachment
msg.attach(MIMEText(TEXT, 'plain'))
filename = os.path.basename(SOURCE)
attachment = open(SOURCE, 'rb')
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename = %s'%filename)
msg.attach(part)
#################### end of attachment part
#################### server part
server = smtp.SMTP(smtp.gmail.com', 587)
server.starttls()
server.login(*send from email*, *send from email password*)
server.sendmail(*send from email*, *send to email*, msg.as_string())
server.close()
But get an AtributeError:
AttributeError: 'list' object has no attribute 'encode'
If i delete Attachment part of code and add msg = MIMEText(TEXT) before server part i get an letter in my email, but it doesn't contain subject. So i get a letter with some text only.
What i do wrong? Any thoughts?
EDIT: The error appears in msg.as_string() line
Here's how I send emails over python
#this will allow us to send emails over our smtp server
import smtplib
#who the message will be from, doesn't really need to be set since the message variable will do this automatically
sender ='sender#email.com'
#who the message will be sent to (this one matters)
receiver = 'receiver#email.com'
#the actual email we'll be sending, note its contents- the To section is irrelevant but neccessary
message = """From: From Person <sender#email.com>
To: To Person <receiver#email.com>
Subject: SMTP Email Test
this is a test email
"""
try:
#proc our server to send the mail by providing it's ip to the program
smtpObj = smtplib.SMTP('<your smtp ip address goes here>')#this is the ip of the server I'm on
#attempt to send the piece of mail
smtpObj.sendmail(sender, receiver, message)
#if the above executes send a confirmation to the user
print "Successfully sent mail!"
except:
#if something goes wrong catch it ad send a message to the user
print "Error: unable to send mail"
to add attachments simply add the following which I retrieved from here
# open the file to be sent
filename = "File_name_with_extension"
attachment = open("Path of the file", "rb")
# instance of MIMEBase and named as p
p = MIMEBase('application', 'octet-stream')
# To change the payload into encoded form
p.set_payload((attachment).read())
# encode into base64
encoders.encode_base64(p)
p.add_header('Content-Disposition', "attachment; filename= %s" % filename)
# attach the instance 'p' to instance 'msg'
smtpObj.attach(p)
Changing:
msg.attach(MIMEText(TEXT, 'plain'))
filename = os.path.basename(SOURCE)
attachment = open(SOURCE, 'rb')
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename = %s'%filename)
msg.attach(part)
with:
msg.attach(MIMEText(text))
for f in files:
with open(f, "rb") as fil:
part = MIMEApplication(fil.read(), Name = basename(f))
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f)
msg.attach(part)
worked for me.
Thanks to How to send email attachments? #Oli answer.

Can't find file to send file as attachment in python

I found this code from another website repository and its used to send emails using python and attach's a file as well. It encodes the file in to base64 brfore sending it. I've tested the code before using an '.xlsx' file and it was sent with out a problem. But now the program doesnt send it for some reason. The file is in the same folder as the code.
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
fromaddr = "FROM EMAIL"
toaddr = "TO EMAIL"
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = toaddr
msg['Subject'] = "SUBJECT"
body = "MESSAGE"
msg.attach(MIMEText(body, 'plain'))
filename = "05-11-2016 - Saturday.xlsx"
attachment = open("05-11-2016 - Saturday", "rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(part)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(fromaddr, "PASSWORD")
text = msg.as_string()
server.sendmail(fromaddr, toaddr, text)
server.quit()
When i run it ths is the error that is outputted:
line 21, in <module>
attachment = open("05-11-2016 - Saturday", "rb")
FileNotFoundError: [Errno 2] No such file or directory: '05-11-2016 - Saturday'
Any help would be appreciated.
you have defined filename in the line above - so why don't you use it? :)
(you forgot the extension 'xlsx' in the open statement)
You now have learned the usefullnes of the DRY-principle:
http://wiki.c2.com/?DontRepeatYourself
By typing it twice, you can change the filename definition and not notice, that the open uses another file...
I just ran your code using my credentials and made a small txt file in the same directory as the code to replicate your conditions. Here's what you need to modify:
filename = "ExplicitFileName.txt"
attachment = open("/USE/COMPLETE/PATH/TO/FILE/ExplicitFileName.txt", "rb")
Or as Ilja pointed out the DRY principle you could do it like this:
filename = "ExplicitFileName.txt"
attachment = open("/COMPLETE/PATH/TO/FILE/" + filename, "rb")
Both of these will work just fine.

Python mail: encoded attachments are truncated

I'm using the following function to send an email message with two attachments in my python script:
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders
...
def sendMail(sender_name, to, subject, text, files=None,server="localhost"):
assert type(to)==list
if files:
assert type(files)==list
print "Files: ",files
fro = sender_name
msg = MIMEMultipart()
msg['From'] = fro
msg['To'] = COMMASPACE.join(to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach( MIMEText(text) )
if files:
for file in files:
# ************** File attaching - Start **************
part = MIMEBase('application', "octet-stream")
part.set_payload( open(file,"rb").read() )
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(file))
msg.attach(part)
# ************** File attaching - End **************
server.set_debuglevel(1)
server.ehlo()
server.starttls()
server.ehlo()
server.sendmail(fro, to, msg.as_string())
server.quit()
I get the mail, and the attachments are there, but for some reason, they are truncated a bit. My guess is I'm missing something in the encoding process.
For example:
Attachment 1: Original file byte count is 1433902, while the new byte count is 1433600
Attachment 2: Original file byte count is 2384703, while the new byte count is 2383872
Any ideas?
Found the problem. Turns out I tried sending the files before the buffer of the writing process was fully flushed.
So, it was a synchronization issue and not an encoding issue.
Sorry about that, and thanks for the help guys!
Could it be related to your current base64.MAXBINSIZE? Encoders.encode_base64 uses base64.encodestring internally. The default value for base64.MAXBINSIZE is 57, can always try setting it larger: base64.MAXBINSIZE = 65536
If the file is already written--be sure to .close() the file and re-open()/.read() it for the payload.
My issues stemmed from timing and this solved the issue for me.

Categories