Can't find file to send file as attachment in python - 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.

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 an email via python from my raspberry pi

id like to send emails regularly from my raspberry pi with a small appended excel file. For this i use my a gmail account. I can send emails but not with something appended.
These following lines are faulty:
SendMail.prepareMail(…)
and
part.set_payload(os.open(file), "rb").read()) -> this is the error i get "IsADirectoryError: [Errno 21] Is a directory: '/'
i hope you can help me with that
import sys, smtplib, os
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.utils import formatdate
from email import encoders
class SendMail(object):
mailadress = 'teststand#gmail.com'
smtpserver = 'smtp.googlemail.com'
username = 'xxx'
password = 'xxx'
def send(self, files):
# Gather information, prepare mail
to = self.mailadress
From = self.mailadress
#Subject contains preview of filenames
if len(files) <= 3: subjAdd = ','.join(files)
if len(files) > 3: subjAdd = ','.join(files[:3]) + '...'
subject = 'Dateiupload: ' + subjAdd
msg = self.prepareMail(From, to, subject, files)
#Connect to server and send mail
server = smtplib.SMTP(self.smtpserver)
server.ehlo() #Has something to do with sending information
server.starttls() # Use encrypted SSL mode
server.ehlo() # To make starttls work
server.login(self.username, self.password)
failed = server.sendmail(From, to, msg.as_string())
server.quit()
def prepareMail(self, From, to, subject, attachments):
msg = MIMEMultipart()
msg['From'] = From
msg['To'] = to
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
# The Body message is empty
msg.attach( MIMEText("") )
for file in attachments:
#We could check for mimetypes here, but I'm too lazy
part = MIMEBase('application', "octet-stream")
part.set_payload( open(os.open(file),"rb").read() )
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(file))
msg.attach(part)
#Delete created Tar
return msg
if __name__ == '__main__':
mymail = SendMail()
# Send all files included in command line arguments
mymail.send(sys.argv[1:])
SendMail.prepareMail("teststand#gmail.com", "teststand#gmail.com", "empfanger#gmx.de", "Titel 1", "/home/pi/Desktop/Teststand/export/protokoll/Protokoll04_May_2020.xlsx")
You are iterating through the variable attachments using the for loop, which is a string. So right now for file in attachment means file would contain every character in the string attachments.
Try:
SendMail.prepareMail("teststand#gmail.com", "teststand#gmail.com", "empfanger#gmx.de", "Titel 1", ["/home/pi/Desktop/Teststand/export/protokoll/Protokoll04_May_2020.xlsx"])
Pass the value of attachments in a list. So when you do for file in attachments, the value of file will be equal to the required location string.

Sending xlsx file using SMTP & Python 3

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())

Python script sending files as they come into the directory and email

Trying to create a piece of python code that monitors a directory for new randomly named csv files.
For some reason I cannot find a way to use the unix trick of *.cvs to work.
and then i need to have to particular file to be emailed and moved to a different directory.
this is my code so far
#!/usr/bin/env python
import os
import base64
import smtplib
import mimetypes
import email
import email.mime.application
import shutil
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEBase import MIMEBase
from email import encoders
#locate file and send it to the email
for file in os.listdir("/root/tmp/"):
if file.endswith(".csv"):
fromaddr = "FROM EMAIL"
toaddr = "TO EMAIL"
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = toaddr
msg['Subject'] = "CSV"
body = "CSV SENT SUCCESSFULLY"
msg.attach(MIMEText(body, 'plain'))
filename = "prod.csv" # <-- trying to keep the file name from below as it finds it
attachment = open("/root/tmp/prod.csv", "rb") # <--- need to send cvs as they come in
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 ADDRESS', 587)
server.starttls()
server.login(fromaddr, "PASSWORD")
text = msg.as_string()
server.sendmail(fromaddr, toaddr, text)
server.quit()
#move to a different folder once sent
path = "/root/tmp/" # <--- using root for testing :)
moveto = "/root/sent/" # <-- destination folder
files = os.listdir(path)
files.sort()
for f in files:
src = path+f
dst = moveto+f
shutil.move(src,dst)

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