python can't send attachment files through email - python

I have the following code which works fine, but it doesn't send the attachment files.
import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.MIMEBase import MIMEBase
from email import Encoders
msg=MIMEMultipart()
def mymail(address,body,format,mylist=None):
msg['To']=address
msg['From']='ggous1#gmail.com'
if format=='txt':
text_msg=MIMEText(body,'plain')
elif format=='html':
text_msg=MIMEText(body,'html')
msg.attach(text_msg)
if mylist is not None:
mylist=[]
fn=[]
for f in range(len(mylist)):
direct=os.getcwd()
os.chdir(direct)
part=MIMEBase('application','octet-stream')
part.set_payload(open(mylist[f],'rb').read())
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(mylist[f]))
fn.append(part)
msg.attach(fn)
srv=smtplib.SMTP('smtp.gmail.com')
srv.set_debuglevel(1)
srv.ehlo()
srv.starttls()
srv.ehlo()
srv.login('username','pass')
srv.sendmail(msg['From'],msg['To'],msg.as_string())
srv.quit()
if __name__=="__main__":
address=raw_input('Enter an address to send email in the form "name#host.com" ')
body=raw_input('Enter the contents of the email')
format=raw_input('The format is txt or html?')
question=raw_input('Do you have any files to attach?Yes or No?')
mylist=[]
if question=='Yes' or question=='yes':
fn=raw_input('Enter filename')
mylist.append(fn)
mymail(address,body,format,mylist)
Am I not using MIMEBase right, or do I have an error in my code?
UPDATE------------------------
if mylist is not None:
mylist=[]
fn=[]
for f in range(len(mylist)):
direct=os.getcwd()
os.chdir(direct)
fn[f]=open(mylist[f],'r')
part=msg.attach(MIMEApplication(fn[f]))
mylist.append(part)

I would recommend to use MIMEApplication instead for the attachment. You also do not need to do all the payload encoding manually since that is already done automatically. This example works for me:
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.utils import formataddr
from email.utils import make_msgid
from email.utils import formatdate
email = MIMEMultipart()
email['From'] = formataddr(('Jane Doe', 'jane#example.com'))
email['Subject'] = u'Test email'
email['Message-Id'] = make_msgid()
email['Date'] = formatdate(localtime=True)
email.attach(MIMEText(u'This is your email contents.'))
email.attach(MIMEApplication('your binary data'))
print email.as_string()
Note that I'm also taking care to set a proper Date and Message-Id header here.
Applying that to your code (and doing a few small cleanups) I get the following working code:
import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
from email.utils import make_msgid
from email.utils import formatdate
def make_mail(address,body,format,mylist=[]):
msg = MIMEMultipart()
msg['To'] = address
msg['From'] = 'ggous1#gmail.com'
msg['Message-Id'] = make_msgid()
msg['Date'] = formatdate(localtime=True)
msg.attach(MIMEText(body, 'plain' if format == 'txt' else 'html'))
for filename in mylist:
part = MIMEApplication(open(filename).read())
part.add_header('Content-Disposition',
'attachment; filename="%s"' % os.path.basename(filename))
msg.attach(part)
return msg
def send_mail(msg):
srv = smtplib.SMTP('smtp.gmail.com')
srv.set_debuglevel(1)
srv.ehlo()
srv.starttls()
srv.ehlo()
srv.login('username','pass')
srv.sendmail(msg['From'], msg['To'], msg.as_string())
srv.quit()
if __name__=="__main__":
address=raw_input('Enter an address to send email in the form "name#host.com" ')
body=raw_input('Enter the contents of the email')
format=raw_input('The format is txt or html?')
question=raw_input('Do you have any files to attach?Yes or No?')
mylist=[]
if question=='Yes' or question=='yes':
fn=raw_input('Enter filename')
mylist.append(fn)
msg = make_mail(address,body,format,mylist)
send_mail(msg)

Related

Sending multiple files function only sends the last file in a list and ignores the others

I have a function that emails multiple files as attachment.
But for some reason it only send the last file specified in a list:
files=['AlignEnvironmentalPremium.txt', 'TestFile.xlsx']
It will only send TestFile.xlsx and ignore AlignEnvironmentalPremium.txt
Do I need to change something in a code so it would send all files in a list and not just the last one?
import os
from base64 import decodebytes
from xlsxwriter.utility import xl_rowcol_to_cell
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
from email import encoders
import datetime
send_from = 'sendfrom#.usercom'
send_to = ['sendto#.usercom']
cc = ['copyto#.usercom']
rcpt = COMMASPACE.join(send_to).split(',') + COMMASPACE.join(cc).split(',')
subject = 'Monthly Audit Report'
text = 'Hello, \nPlease, see attached'
files=['AlignEnvironmentalPremium.txt', 'TestFile.xlsx']
# below function sends email
def send_mail(send_from,rcpt,subject,text ,files):
assert isinstance(send_to, list)
assert isinstance(cc, list)
assert isinstance(files, list)
msg = MIMEMultipart()
msg["From"] = send_from
msg["To"] = COMMASPACE.join(send_to)
msg['Cc'] = COMMASPACE.join(cc)
msg["Date"] = formatdate(localtime=True)
msg["Subject"] = subject
msg.attach(MIMEText(text))
if files is not None:
for f in files:
part = MIMEBase("application", "octet-stream")
part.set_payload(open(f, "rb").read())
encoders.encode_base64(part)
part.add_header(
"Content-Disposition", 'attachment; filename="%s"' % os.path.basename(f)
)
msg.attach(part)
smtp = smtplib.SMTP('0.0.0.0: 25')
smtp.sendmail(send_from, rcpt, msg.as_string())
smtp.close()
send_mail(send_from,rcpt,subject,text,files)
I'm not familiar with the email library so maybe there's a nuance I'm missing, however I think this simply a syntax slip-up.
You're creating an instance of MIMEBase for each file and calling it part. At the end of your for loop the value of part will be the last file that you iterated over. Since msg.attach(part) is outside your for loop you will only be attaching the last file in your list of files.
I think you know where to go from here.

How can i send a text or word file using the smtplib in Python?

i searched on google but these examples are kind of tough for beginners like me..If there is any simple example of it...I know how send the html based messages
import smtplib
from os.path import basename
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
def send_mail(send_from,password send_to, subject, text, file=None,
server="127.0.0.1"):
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = COMMASPACE.join(send_to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach(MIMEText(text))
with open(file, "rb") as fil:
part = MIMEApplication(
fil.read(),
Name=basename(file)
)
# After the file is closed
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(file)
msg.attach(part)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(send_from, password )
text = msg.as_string()
server.sendmail(send_from, send_to, text)
server.quit()
send_mail('From','Password','ToMail','Subject','msg',r'FullPath')

How to sign and send a Mimultipart message in python?

Typically, sending a message with attachments is performed like this in python.
import smtplib
from os.path import basename
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
def send_mail(send_from, send_to, subject, text, files=None,
server="127.0.0.1"):
assert isinstance(send_to, list)
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = COMMASPACE.join(send_to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach(MIMEText(msg, 'html'))
for f in files or []:
with open(f, "rb") as fil:
part = MIMEApplication(
fil.read(),
Name=basename(f)
)
# After the file is closed
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f)
msg.attach(part)
smtp = smtplib.SMTP(server)
smtp.sendmail(send_from, send_to, msg.as_string()) # The problem is here
smtp.close()
However, I cannot perform that since the part to be signed with ꜱᴍɪᴍᴇ is only the body and attachments, not all metadata.
In such situation, I would need to split message and Metadata. However, I still need to use email.mime.multipart and email.mime.application`` for constructing attachments metadata (while excluding other metadata)
So how to create an ʜᴛᴍʟ e‑mail with attachments while still using ʜᴛᴍʟ formatting and attachments ?

Adding email attachments using smtplib

I'm trying to send emails and attach a VCF file, but I'm running into some trouble. I've managed to send emails with plain text without any issues, but here's the error I'm getting when I run my code now:
AttributeError: 'file' object has no attribute 'rfind'
And my code:
import vobject
import requests
import smtplib
from os.path import basename
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
j = vobject.vCard()
j.add('n')
j.n.value = vobject.vcard.Name(family='Harris', given='Jeffrey')
j.add('fn')
j.fn.value = 'Jeffrey Harris'
j.add('email')
j.email.value = 'jeffrey#osafoundation.org'
j.email.type_param = 'Internet'
k = j.serialize()
with open ('new.vcf', 'w') as file:
file.write(k)
with open('new.vcf', 'rb') as fil:
part = MIMEApplication(
fil.read(),
Name=basename(fil)
)
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(fil)
msg = MIMEMultipart()
msg['From'] = 'me#myemail.com'
msg['To'] = 'me#myemail.com'
msg['Subject'] = 'test'
message = 'test'
msg.attach(part)
mailserver = smtplib.SMTP('secure.emailsrvr.com',587)
mailserver.ehlo()
mailserver.starttls()
mailserver.ehlo()
mailserver.login('me#myemail.com', 'mypassword')
mailserver.sendmail('me#myemail.com','me#myemail.com',msg.as_string())
mailserver.quit()
Any ideas on what I'm doing wrong?
Your problem is in Name=basename(fil) because basename() accept str, bytes or os.PathLike object.
You are trying to pass _io.BudderReader as an argument.
Solution:
You should pass the filename of the attachment (in OP case is new.vcf).

e-mail large text file

I have a text file to be sent via e-mail. I used the following code to send e-mail through smtplib. This code prints the attachment as e-mail body. Since my text file is bit larger all the content is not visible in mail body? How to display all the content in e-mail body? Any suggestions?
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
msg = MIMEMultipart()
msg['Subject'] = 'ANALYSIS REPORT'
filename = "report.txt"
f = file(filename)
attachment = MIMEText(f.read())
msg.attach(attachment)
smtpObj = smtplib.SMTP('mail.my-domain.com', 25)
smtpObj.sendmail(sender, receivers, msg.as_string())
print "e-mail Successfully Sent!"
I would try to compress the content body, maybe that would get the message size down enough to get the mail through.
Example:
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.mime.application import MIMEApplication
from email.MIMEImage import MIMEImage
import io
import gzip
msg = MIMEMultipart()
msg['Subject'] = 'ANALYSIS REPORT'
msg.attach(MIMEText('report attached'))
filename = "report.txt"
with open(filename, 'rb') as f, io.BytesIO() as b:
g = gzip.GzipFile(mode='wb', fileobj=b)
g.writelines(f)
g.close()
attachment = MIMEApplication(b.getvalue(), 'x-gzip')
attachment['Content-Disposition'] = 'attachment; filename=report.txt.gz'
msg.attach(attachment)
smtpObj = smtplib.SMTP('mail.my-domain.com', 25)
print smtpObj.sendmail(sender, receivers, msg.as_string())
print "e-mail Successfully Sent!"

Categories