¿Cómo envío archivos adjuntos usando SMTP?

Quiero escribir un progtwig que envíe correo electrónico usando el smtplib de Python. Busqué en el documento y en los RFC, pero no pude encontrar nada relacionado con los archivos adjuntos. Por lo tanto, estoy seguro de que hay un concepto de nivel superior que me estoy perdiendo. ¿Alguien me puede dar una idea de cómo funcionan los archivos adjuntos en SMTP?

Lo que quieres revisar es el módulo de email . Le permite crear mensajes compatibles con MIME que luego envía con smtplib.

Aquí hay un ejemplo de un mensaje con un archivo PDF adjunto, un “cuerpo” de texto y envío a través de Gmail.

 # Import smtplib for the actual sending function import smtplib # For guessing MIME type import mimetypes # Import the email modules we'll need import email import email.mime.application # Create a text/plain message msg = email.mime.Multipart.MIMEMultipart() msg['Subject'] = 'Greetings' msg['From'] = 'xyz@gmail.com' msg['To'] = 'abc@gmail.com' # The main body is just another attachment body = email.mime.Text.MIMEText("""Hello, how are you? I am fine. This is a rather nice letter, don't you think?""") msg.attach(body) # PDF attachment filename='simple-table.pdf' fp=open(filename,'rb') att = email.mime.application.MIMEApplication(fp.read(),_subtype="pdf") fp.close() att.add_header('Content-Disposition','attachment',filename=filename) msg.attach(att) # send via Gmail server # NOTE: my ISP, Centurylink, seems to be automatically rewriting # port 25 packets to be port 587 and it is trashing port 587 packets. # So, I use the default port 25, but I authenticate. s = smtplib.SMTP('smtp.gmail.com') s.starttls() s.login('xyz@gmail.com','xyzpassword') s.sendmail('xyz@gmail.com',['xyz@gmail.com'], msg.as_string()) s.quit() 

Aquí hay un ejemplo que saqué de una aplicación de trabajo que hicimos. Crea un correo electrónico HTML con un archivo adjunto de Excel.

  import smtplib,email,email.encoders,email.mime.text,email.mime.base smtpserver = 'localhost' to = ['email@somewhere.com'] fromAddr = 'automated@hi.com' subject = "my subject" # create html email html = '' html +='

...

' html += "" emailMsg = email.MIMEMultipart.MIMEMultipart('alternative') emailMsg['Subject'] = subject emailMsg['From'] = fromAddr emailMsg['To'] = ', '.join(to) emailMsg['Cc'] = ", ".join(cc) emailMsg.attach(email.mime.text.MIMEText(html,'html')) # now attach the file fileMsg = email.mime.base.MIMEBase('application','vnd.ms-excel') fileMsg.set_payload(file('exelFile.xls').read()) email.encoders.encode_base64(fileMsg) fileMsg.add_header('Content-Disposition','attachment;filename=anExcelFile.xls') emailMsg.attach(fileMsg) # send email server = smtplib.SMTP(smtpserver) server.sendmail(fromAddr,to,emailMsg.as_string()) server.quit()

Bueno, los archivos adjuntos no se tratan de ninguna manera especial, son “solo” hojas del árbol de objetos de mensajes. Puede encontrar las respuestas a cualquier pregunta sobre mesasges compatibles con MIME en esta sección de la documentación en el paquete de correo electrónico de python.

En general, cualquier tipo de adjunto (leído: datos binarios sin procesar) se puede representar mediante el uso de la encoding Content-Transfer-Encoding base64 (o similar).

A continuación, le indicamos cómo enviar correos electrónicos con archivos adjuntos en formato zip y un cuerpo y un asunto con encoding utf-8.

No fue fácil descifrar esto, debido a la falta de documentación y muestras para este caso en particular.

Los caracteres que no son ASCII en respuesta a deben codificarse, por ejemplo, con ISO-8859-1. Probablemente existe una función que puede hacer esto.

Propina:
Envíese un correo electrónico, guárdelo y examine el contenido para descubrir cómo hacer lo mismo en Python.

Aquí está el código, para Python 3:

 #!/usr/bin/env python3 # -*- coding: utf-8 -*- # vim:set ts=4 sw=4 et: from os.path import basename from smtplib import SMTP from email.mime.text import MIMEText from email.mime.base import MIMEBase from email.mime.multipart import MIMEMultipart from email.header import Header from email.utils import parseaddr, formataddr from base64 import encodebytes def send_email(recipients=["somebody@somewhere.xyz"], subject="Test subject æøå", body="Test body æøå", zipfiles=[], server="smtp.somewhere.xyz", username="bob", password="password123", sender="Bob ", replyto="=?ISO-8859-1?Q?M=F8=F8=F8?= "): #: bool """Sends an e-mail""" to = ",".join(recipients) charset = "utf-8" # Testing if body can be encoded with the charset try: body.encode(charset) except UnicodeEncodeError: print("Could not encode " + body + " as " + charset + ".") return False # Split real name (which is optional) and email address parts sender_name, sender_addr = parseaddr(sender) replyto_name, replyto_addr = parseaddr(replyto) sender_name = str(Header(sender_name, charset)) replyto_name = str(Header(replyto_name, charset)) # Create the message ('plain' stands for Content-Type: text/plain) try: msgtext = MIMEText(body.encode(charset), 'plain', charset) except TypeError: print("MIMEText fail") return False msg = MIMEMultipart() msg['From'] = formataddr((sender_name, sender_addr)) msg['To'] = to #formataddr((recipient_name, recipient_addr)) msg['Reply-to'] = formataddr((replyto_name, replyto_addr)) msg['Subject'] = Header(subject, charset) msg.attach(msgtext) for zipfile in zipfiles: part = MIMEBase('application', "zip") b = open(zipfile, "rb").read() # Convert from bytes to a base64-encoded ascii string bs = encodebytes(b).decode() # Add the ascii-string to the payload part.set_payload(bs) # Tell the e-mail client that we're using base 64 part.add_header('Content-Transfer-Encoding', 'base64') part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(zipfile)) msg.attach(part) s = SMTP() try: s.connect(server) except: print("Could not connect to smtp server: " + server) return False if username: s.login(username, password) print("Sending the e-mail") s.sendmail(sender, recipients, msg.as_string()) s.quit() return True def main(): send_email() if __name__ == "__main__": main() 
 # -*- coding: utf-8 -*- """ Mail sender """ from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText import smtplib import pystache import codecs import time import sys reload(sys) sys.setdefaultencoding('utf-8') HOST = 'smtp.exmail.qq.com' PORT = 587 USER = 'your@mail.com' PASS = 'yourpass' FROM = 'your@mail.com' SUBJECT = 'subject' HTML_NAME = 'tpl.html' CSV_NAME = 'list.txt' FAILED_LIST = [] def send(mail_receiver, mail_to): # text = mail_text html = render(mail_receiver) # msg = MIMEMultipart('alternative') msg = MIMEMultipart('mixed') msg['From'] = FROM msg['To'] = mail_to.encode() msg['Subject'] = SUBJECT.encode() # msg.attach(MIMEText(text, 'plain', 'utf-8')) msg.attach(MIMEText(html, 'html', 'utf-8')) try: _sender = smtplib.SMTP( HOST, PORT ) _sender.starttls() _sender.login(USER, PASS) _sender.sendmail(FROM, mail_to, msg.as_string()) _sender.quit() print "Success" except smtplib.SMTPException, e: print e FAILED_LIST.append(mail_receiver + ',' + mail_to) def render(name): _tpl = codecs.open( './html/' + HTML_NAME, 'r', 'utf-8' ) _html_string = _tpl.read() return pystache.render(_html_string, { 'receiver': name }) def main(): ls = open('./csv/' + CSV_NAME, 'r') mail_list = ls.read().split('\r') for _receiver in mail_list: _tmp = _receiver.split(',') print 'Mail: ' + _tmp[0] + ',' + _tmp[1] time.sleep(20) send(_tmp[0], _tmp[1]) print FAILED_LIST main()