I would like to create a text file in Python, write something to it, and then e-mail it out (put test.txt as an email attachment).
However, I cannot save this file locally. Does anyone know how to go about doing this?
As soon as I open the text file to write in, it is saved locally on my computer.
f = open("test.txt","w+")
I am using smtplib and MIMEMultipart to send the mail.
StringIO is the way to go...
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from io import StringIO
email = MIMEMultipart()
email['Subject'] = 'subject'
email['To'] = 'recipient#example.com'
email['From'] = 'sender#example.com'
# Add the attachment to the message
f = StringIO()
# write some content to 'f'
f.write("content for 'test.txt'")
f.seek(0)
msg = MIMEBase('application', "octet-stream")
msg.set_payload(f.read())
encoders.encode_base64(msg)
msg.add_header('Content-Disposition',
'attachment',
filename='test.txt')
email.attach(msg)
I found this post while figuring out how to do this with the newer EmailMessage, which was introduced in Python 3.6 (see https://docs.python.org/3/library/email.message.html). It's slightly less code:
from email.message import EmailMessage
from io import StringIO
from smtplib import SMTP
message = EmailMessage()
message['Subject'] = 'Subject'
message['From'] = 'from#example.com'
message['To'] = 'to#example.com'
f = StringIO()
f.name = 'attachment.txt'
f.write('contents of file')
f.seek(0)
message.add_attachment(f.read(), filename=f.name)
with SMTP('yourmailserver.com', 25) as server:
server.send_message(message)
Related
Script is working, i received zip file. But when i am trying to open zip file (WinRaR and 7-zip) i get error - archive is either in unknown format or damaged. Maybe problem related with encoding ???
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
import smtplib
def send_mail():
sender = '*****#mail.ru'
password = '*************'
smtp_obj = smtplib.SMTP('smtp.mail.ru', 587)
smtp_obj.starttls()
msg = MIMEMultipart()
msg['Subject'] = 'Data from Experiment'
msg['From'] = sender
msg['To'] = sender
with open(r'C:\Users\Admin\Desktop\Python\Python + SQL\Send_email\arch\arch.zip',encoding='CP866') as file:
# Attach the file with filename to the email
msg.attach(MIMEApplication(file.read(), Name='arch.zip'))
# Login to the server
smtp_obj.login(sender, password)
# Convert the message to a string and send it
smtp_obj.sendmail(sender, sender, msg.as_string())
#smtp_obj.quit()
send_mail()
Do not pass an encoding when opening a zip file. That's binary data and should be treated as such! open(..., 'b')
I would like to ask for an advice for adding a zip option to the mailing script I am using for the delivery of reports.
I have email attachment limit set to 25MB and therefore some reports in json format that exceeds 25MB are dropped by the mailer script. I wanted to add a zip support that will compress the attachment if that is bigger than for ex. 15MB.
Below code is mailer part only.
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
from email.utils import COMMASPACE
import sys
from os.path import basename
import argparse
import conf
import os
import zipfile
import zlib
###############################################################################
# Mailer function for server running environment
def send_mail(recipients, cc=None, subject=None, attachment=None, body=None, send_from=None):
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = send_from
msg['To'] = ','.join(recipients)
if cc:
msg['CC'] = ','.join(cc)
recipients.extend(cc)
if body:
text = MIMEText(body, 'html')
msg.attach(text)
if attachment:
with open(attachment, 'rb') as fp:
att = MIMEApplication(fp.read(), Name=basename(attachment))
att['Content-Disposition'] = f'attachment; filename="{basename(attachment)}"'
msg.attach(att)
smtp = smtplib.SMTP(conf.RELAY)
smtp.sendmail(send_from, recipients, msg.as_string())
smtp.quit()
Assuming the code you already have correctly attaches arbitrary attachments, including binary ones, then the following should zip up an attchment that is > 15M. If the original file's base name was, for example, test.dat, then the attachment archive name will be test.dat.zip and will contain test.dat as the single compressed file implicitly using zlib. No temporary files are needed for this as an in-memory buffer is used. So if the attachment is not outrageously large, this should be quite efficient:
if attachment:
MAX_ATTACHMENT_SIZE = 15 * 1024 * 1024
with open(attachment, 'rb') as fp:
bytes = fp.read()
attachment_name = basename(attachment)
if len(bytes) > MAX_ATTACHMENT_SIZE:
from io import BytesIO
buffer = BytesIO()
with zipfile.ZipFile(buffer, 'w', compression=zipfile.ZIP_DEFLATED) as zip_file:
zip_file.writestr(attachment_name, bytes)
# New attachment name and new data:
attachment_name += '.zip'
bytes = buffer.getvalue()
att = MIMEApplication(bytes, Name=attachment_name)
att['Content-Disposition'] = f'attachment; filename="{attachment_name}"'
msg.attach(att)
If you want to zip attacment, you can use the zipfile module to compress the file.
import zipfile
import tempfile
from email import encoders
from email.message import Message
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
...
if attachment:
zf = tempfile.TemporaryFile(prefix='mail', suffix='.zip')
zip = zipfile.ZipFile(zf, 'w')
zip.write(attachment)
zip.close()
zf.seek(0)
att = MIMEBase('application', 'zip')
att.set_payload(zf.read())
encoders.encode_base64(att)
att.add_header('Content-Disposition', 'attachment', filename=the_file + '.zip')
msg.attach(att)
...
I need to figure out how to make a script that scans for new files in a directory, and when there is a new one, sends the file via email.
Someone keeps stealing bikes in my apartment building! First it was my fault (I for got to lock it), now the crook upgraded by cutting chains. I had it after the crook stole my second bike by cutting 1/2 inch airplane wire.
Anyway, using a raspberry pi as a motion activated security camera, I want it to send me a video file as soon as the video program finishes recording it. This is incase they steal the pi.
I am looking at these examples, but I can't figure how to make the script run continuously (every minute) or how to make it scan a folder for a new file.
How do I send attachments using SMTP?
OK
I got it down to scanning and then trying to email. It fails when trying to attach the video file. Can you help? Here is the revised code:
The failure is:
msg = MIMEMultipart()
TypeError: 'LazyImporter' object is not callable, line 38
import time, glob
import smtplib
import email.MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders, MIMEMultipart
import os
#Settings:
fromemail= "Jose Garcia <somerandomemail#gmail.com>"
loginname="somerandomemail#gmail.com"
loginpassword="somerandomepassword"
toemail= "Jose Garcia <somerandomemail#gmail.com>"
SMTPserver='smtp.gmail.com'
SMTPort=587
fileslocation="/Users/someone/Desktop/Test/*.mp4"
subject="Security Notification"
def mainloop():
files=glob.glob(fileslocation) #Put whatever path and file format you're using in there.
while 1:
new_files=glob.glob(fileslocation)
if len(new_files)>len(files):
for x in new_files:
if x in files:
print("New file detected "+x)
print("about to call send email")
sendMail(loginname, loginpassword, toemail, fromemail, subject, gethtmlcode(), x, SMTPserver, SMTPort)
files=new_files
time.sleep(1)
def sendMail(login, password, to, frome, subject, text, filee, server, port):
# assert type(to)==list
# assert type(filee)==list
msg = MIMEMultipart()
msg['From'] = frome
msg['To'] = COMMASPACE.join(to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach( MIMEText(text) )
# #for filee in files:
part = MIMEBase('application', "octet-stream")
part.set_payload( open(filee,"rb").read() )
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"'
% os.path.basename(filee))
msg.attach(part)
smtp = smtplib.SMTP(SMTPserver, SMTPort)
smtp.sendmail(frome, to, msg.as_string() )
server.set_debuglevel(1)
server.starttls()
server.ehlo()
server.login(login, password)
server.sendmail(frome, to, msg)
server.quit()
def gethtmlcode():
print("about to assemble html")
html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
html +='"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">'
html +='<body style="font-size:12px;font-family:Verdana"><p>A new video file has been recorded </p>'
html += "</body></html>"
return(html)
#Execute loop
mainloop()
I finally got it working. I had to use python 2.5 to get rid of the LazyImporter error. Every time a new file is added to the security folder, it gets emailed to me. Logging is broken.
import time, glob
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEMultipart import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
import datetime
from email import Encoders
import os
import sys
#Settings:
fromemail= 'RaspberryPI Security Camera <someone>'
loginname='someone#gmail.com'
loginpassword='something'
toemail= 'Jose Garcia < someone#gmail.com>'
SMTPserver='smtp.gmail.com'
SMTPort=587
fileslocation='/Users/someone/Desktop/Test/*.*'
subject="Security Notification"
log='logfile.txt'
def mainloop():
files=glob.glob(fileslocation) #Put whatever path and file format you're using in there.
while 1:
f = open(log, 'w')
sys.stdout = Tee(sys.stdout, f)
new_files=glob.glob(fileslocation)
if len(new_files)>len(files):
for x in new_files:
if x in files:
print(str(datetime.datetime.now()) + "New file detected "+x)
sendMail(loginname, loginpassword, toemail, fromemail, subject, gettext(), x, SMTPserver, SMTPort)
files=new_files
f.close()
time.sleep(1)
def sendMail(login, password, send_to, send_from, subject, text, send_file, server, port):
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = COMMASPACE.join(send_to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach( MIMEText(text) )
part = MIMEBase('application', "octet-stream")
part.set_payload( open(send_file,"rb").read() )
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(send_file))
msg.attach(part)
smtp = smtplib.SMTP(SMTPserver, SMTPort)
smtp.set_debuglevel(1)
smtp.ehlo()
smtp.starttls()
smtp.login(login, password)
smtp.sendmail(send_from, send_to, msg.as_string() )
smtp.close()
def gettext():
text = "A new file has been added to the security footage folder. \nTime Stamp: "+ str(datetime.datetime.now())
return(text)
class Tee(object):
def __init__(self, *files):
self.files = files
def write(self, obj):
for f in self.files:
f.write(obj)
#Execute loop
mainloop()
It looks like the email module has been refactored over time. This fixed the 'LazyImporter' object not callable error for me on Python 2.7:
from email.mime.text import MIMEText
Noteably it was not happy with (what I thought were) synonyms like import email; email.mime.text.MIMEText(...)
I use python3 and I could not for the life of me get any of these examples to work, but I was able to come up with something that works and is a whole lot simpler.
import smtplib
from email.message import EmailMessage
from email.mime.base import MIMEBase
from email import encoders
# Defining Objects and Importing Files ------------------------------------
# Initializing video object
video_file = MIMEBase('application', "octet-stream")
# Importing video file
video_file.set_payload(open('video.mkv', "rb").read())
# Encoding video for attaching to the email
encoders.encode_base64(video_file)
# creating EmailMessage object
msg = EmailMessage()
# Loading message information ---------------------------------------------
msg['From'] = "person_sending#gmail.com"
msg['To'] = "person_receiving#gmail.com"
msg['Subject'] = 'text for the subject line'
msg.set_content('text that will be in the email body.')
msg.add_attachment(video_file, filename="video.mkv")
# Start SMTP Server and sending the email ---------------------------------
server=smtplib.SMTP_SSL('smtp.gmail.com',465)
server.login("person_sending#gmail.com", "some-clever-password")
server.send_message(msg)
server.quit()
Just put your script in a loop and have it sleep for 60 seconds. You can use glob to get a list of files in the directory. in is pretty useful for seeing what is in a list (i.e. the list of files in the directory).
import time, glob
files=glob.glob("/home/me/Desktop/*.mp4") #Put whatever path and file format you're using in there.
while 1:
new_files=glob.glob("/home/me/Desktop/*.mp4")
if len(new_files)>len(files):
for x in new_files:
if x not in files:
print("New file: "+x) #This is where you would email it. Let me know if you need help figuring that out.
files=new_files
time.sleep(60)
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
msg = MIMEMultipart('multipart/related')
fromaddr = 'from#gmail.com'
toaddrs = 'to#gmail.com'
#provide gmail user name and password
username = 'to#gmail.com'
password = 'messifan'
filename = "1.jpg"
f = file(filename)
attachment = MIMEImage(f.read()) # error here
attachment.add_header('Content-Disposition', 'attachment', filename=filename)
.
.
server.sendmail(fromaddr, toaddrs, msg.as_string())
i am using this code to send email. i can attach text file using this script.(chenging MIMEImage to MIMEtext). but cannot attach image. the error is Could not guess Image mime subtype
Try
attachment = MIMEImage(f.read(), _subtype="jpeg") # error here
a bit of a guess here, but maybe try opening the file in binary mode?
f = file(filename, 'rb')
Looks to me like you created a "file" object, but you never opened it.
Where you have:
f = file(filename)
attachment = MIMEImage(f.read()) # error here
I think you instead need:
fp = open(filename, 'rb')
attachment = MIMEImage(fp.read())
fp.close()
I am trying to send an email using the Python email client. I have written the following code but it sends the attachemnt as the body and not as an attached file.
Could someone please tell me what is wrong with the code:
# Import smtplib for the actual sending function
import smtplib
# Here are the email package modules we'll need
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
EMAIL_LIST = ['rec#rec.com']
# Create the container (outer) email message.
msg = MIMEMultipart()
msg['Subject'] = 'THIS DOES NOT WORK'
# me == the sender's email address
# family = the list of all recipients' email addresses
msg['From'] = 'send#sender.com'
print EMAIL_LIST
print '--------------------'
print ', '.join(EMAIL_LIST)
msg['To'] = ', '.join(EMAIL_LIST)
msg.preamble = 'THIS DOES NOT WORK'
fileName = 'c:\\p.trf'
with open(fileName, 'r') as fp:
attachment = MIMEText(fp.read())
fp.close()
msg.add_header('Content-Disposition', 'attachment', filename=fileName)
msg.attach(attachment)
# Send the email via our own SMTP server.
s = smtplib.SMTP('localhost')
s.sendmail('julka#pv.com', EMAIL_LIST, msg.as_string())
s.quit()
For the attachment, you should probably use, MIMEBase something like this:
import os
from email import encoders
from email.mime.base import MIMEBase
with open(fileName,'r') as fp:
attachment = MIMEBase('application','octet-stream')
attachment.set_payload(fp.read())
encoders.encode_base64(attachment)
attachment.add_header('Content-Disposition','attachment',filename=os.path.split(fileName)[1])
msg.attach(attachment)