¿Cómo enviar archivos adjuntos de correo electrónico?

283

Tengo problemas para entender cómo enviar un archivo adjunto por correo electrónico usando Python. He enviado con éxito mensajes simples con el smtplib. ¿Podría alguien explicar cómo enviar un archivo adjunto en un correo electrónico? Sé que hay otras publicaciones en línea, pero como principiante de Python, me resulta difícil entenderlas.

Ricardo
fuente
55
Aquí hay una implementación simple que puede adjuntar múltiples archivos, e incluso referirse a ellos en el caso de imágenes para incrustar. datamakessense.com/…
AdrianBR
Encontré esto útil drupal.org/project/mimemail/issues/911612 resulta que los adjuntos de imágenes deben adjuntarse a una parte secundaria de tipo relacionado. Si adjunta la imagen a la parte MIME raíz, las imágenes pueden aparecer en la lista de elementos adjuntos y previsualizarse en clientes como outlook365.
Hinchy
@AdrianBR, ¿y si tengo una imagen como archivo PDF? Los PNG tienen problemas de píxeles al hacer zoom, por lo que los png no son buenos para mí.
Pinocho

Respuestas:

416

Aquí está otro:

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

    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())
    smtp.close()

Es muy parecido al primer ejemplo ... Pero debería ser más fácil pasar.

Oli
fuente
77
Tenga cuidado con los valores predeterminados mutables: stackoverflow.com/questions/101268/hidden-features-of-python/…
Gringo Suave
11
@ user589983 ¿Por qué no sugerir una edición como lo haría cualquier otro usuario aquí? He cambiado la referencia restante a fileen f.
Oli
9
Aviso para desarrolladores de Python3: el módulo "email.Utils" ha cambiado de nombre a "email.utils"
gecco
77
para python2.5 + es más fácil usar la aplicación MIMEA en su lugar: reduce las tres primeras líneas del ciclo a:part = MIMEApplication(open(f, 'rb').read())
mata
55
El asunto no se mostró en el correo electrónico enviado. Funcionó solo después de cambiar la línea a msg ['Asunto'] = asunto. Yo uso Python 2.7.
Lucas
70

Aquí está la versión modificada de OliPython 3

import smtplib
from pathlib import Path
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


def send_mail(send_from, send_to, subject, message, files=[],
              server="localhost", port=587, username='', password='',
              use_tls=True):
    """Compose and send email with provided info and attachments.

    Args:
        send_from (str): from name
        send_to (list[str]): to name(s)
        subject (str): message title
        message (str): message body
        files (list[str]): list of file paths to be attached to email
        server (str): mail server host name
        port (int): port number
        username (str): server auth username
        password (str): server auth password
        use_tls (bool): use TLS mode
    """
    msg = MIMEMultipart()
    msg['From'] = send_from
    msg['To'] = COMMASPACE.join(send_to)
    msg['Date'] = formatdate(localtime=True)
    msg['Subject'] = subject

    msg.attach(MIMEText(message))

    for path in files:
        part = MIMEBase('application', "octet-stream")
        with open(path, 'rb') as file:
            part.set_payload(file.read())
        encoders.encode_base64(part)
        part.add_header('Content-Disposition',
                        'attachment; filename="{}"'.format(Path(path).name))
        msg.attach(part)

    smtp = smtplib.SMTP(server, port)
    if use_tls:
        smtp.starttls()
    smtp.login(username, password)
    smtp.sendmail(send_from, send_to, msg.as_string())
    smtp.quit()
Ehsan Iran-Nejad
fuente
gracias, pero sería bueno tener también lo básico: la sintaxis para un solo archivo adjunto (usando su ruta)
JinSnow
Parece que no cierra sus archivos, será basura recolectada o cerrada a la salida, pero es un mal hábito. con open () como f: es la forma correcta.
comte
El código no funciona. El nombre de variable incorrecto f en formato (os.path.basename (f)) debe ser format (os.path.basename (path))
Chris
1
send_to debería ser list [str]
Subin
2
mejor respuesta para mí, pero hay un pequeño error: reemplazar import pathlibconfrom pathlib import Path
AleAve81
65

Este es el código que terminé usando:

import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email import Encoders


SUBJECT = "Email Data"

msg = MIMEMultipart()
msg['Subject'] = SUBJECT 
msg['From'] = self.EMAIL_FROM
msg['To'] = ', '.join(self.EMAIL_TO)

part = MIMEBase('application', "octet-stream")
part.set_payload(open("text.txt", "rb").read())
Encoders.encode_base64(part)

part.add_header('Content-Disposition', 'attachment; filename="text.txt"')

msg.attach(part)

server = smtplib.SMTP(self.EMAIL_SERVER)
server.sendmail(self.EMAIL_FROM, self.EMAIL_TO, msg.as_string())

El código es muy similar a la publicación de Oli. Gracias a todos

Código basado en la publicación del problema de archivo adjunto de correo electrónico binario .

Ricardo
fuente
2
Buena respuesta. Sería bueno si también contuviera código que agregue un texto de cuerpo de muestra.
Steven Bluen
44
Tenga en cuenta que en las versiones modernas de la biblioteca de correo electrónico, las importaciones del módulo son diferentes. por ejemplo:from email.mime.base import MIMEBase
Varun Balupuri
27
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
import smtplib

msg = MIMEMultipart()
msg.attach(MIMEText(file("text.txt").read()))
msg.attach(MIMEImage(file("image.png").read()))

# to send
mailer = smtplib.SMTP()
mailer.connect()
mailer.sendmail(from_, to, msg.as_string())
mailer.close()

Adaptado de aquí .

Oli
fuente
No es exactamente lo que estoy buscando. El archivo fue enviado como el cuerpo de un correo electrónico. También faltan paréntesis en las líneas 6 y 7. Sin embargo
Richard
2
Los correos electrónicos son texto sin formato, y eso es lo que smtplibadmite. Para enviar archivos adjuntos, los codifica como un mensaje MIME y los envía en un correo electrónico de texto sin formato. Sin embargo, hay un nuevo módulo de correo electrónico de Python: docs.python.org/library/email.mime.html
Katriel el
@katrienlalex un ejemplo de trabajo ayudaría mucho a mi comprensión
Richard
1
¿Estás seguro de que el ejemplo anterior no funciona? No tengo un servidor SMTP a mano, pero lo miré msg.as_string()y ciertamente parece el cuerpo de un correo electrónico MIME multiparte. Wikipedia explica MIME: en.wikipedia.org/wiki/MIME
Katriel
1
Line 6, in <module> msg.attach(MIMEText(file("text.txt").read())) NameError: name 'file' is not defined
Benjamin2002
7

Otra forma con python 3 (si alguien está buscando):

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 = "sender mail address"
toaddr = "receiver mail address"

msg = MIMEMultipart()

msg['From'] = fromaddr
msg['To'] = toaddr
msg['Subject'] = "SUBJECT OF THE EMAIL"

body = "TEXT YOU WANT TO SEND"

msg.attach(MIMEText(body, 'plain'))

filename = "fileName"
attachment = open("path of file", "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, "sender mail password")
text = msg.as_string()
server.sendmail(fromaddr, toaddr, text)
server.quit()

Asegúrese de permitir " aplicaciones menos seguras " en su cuenta de Gmail

Sudarshan
fuente
6

Versión de Gmail, trabajando con Python 3.6 (tenga en cuenta que necesitará cambiar la configuración de Gmail para poder enviar correos electrónicos a través de smtp desde ella:

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from os.path import basename


def send_mail(send_from: str, subject: str, text: str, 
send_to: list, files= None):

    send_to= default_address if not send_to else send_to

    msg = MIMEMultipart()
    msg['From'] = send_from
    msg['To'] = ', '.join(send_to)  
    msg['Subject'] = subject

    msg.attach(MIMEText(text))

    for f in files or []:
        with open(f, "rb") as fil: 
            ext = f.split('.')[-1:]
            attachedfile = MIMEApplication(fil.read(), _subtype = ext)
            attachedfile.add_header(
                'content-disposition', 'attachment', filename=basename(f) )
        msg.attach(attachedfile)


    smtp = smtplib.SMTP(host="smtp.gmail.com", port= 587) 
    smtp.starttls()
    smtp.login(username,password)
    smtp.sendmail(send_from, send_to, msg.as_string())
    smtp.close()

Uso:

username = '[email protected]'
password = 'top-secret'
default_address = ['[email protected]'] 

send_mail(send_from= username,
subject="test",
text="text",
send_to= None,
files= # pass a list with the full filepaths here...
)

Para usar con cualquier otro proveedor de correo electrónico, simplemente cambie las configuraciones de smtp.

Ferrarezi
fuente
4

El código más simple al que podría llegar es:

#for attachment email
from django.core.mail import EmailMessage

    def attachment_email(request):
            email = EmailMessage(
            'Hello', #subject
            'Body goes here', #body
            '[email protected]', #from
            ['[email protected]'], #to
            ['[email protected]'], #bcc
            reply_to=['[email protected]'],
            headers={'Message-ID': 'foo'},
            )

            email.attach_file('/my/path/file')
            email.send()

Se basó en la documentación oficial de Django.

Andrade
fuente
3
en su caso, debe instalar django para enviar un correo electrónico ... no responde correctamente la pregunta
comte
@comte 'coz python solo se usa para Django, ¿verdad?
Auspex
55
@Auspex ese es mi punto ;-) es como instalar LibreOffice para editar un archivo de configuración ...
comte
Esto me parece útil e informativo. solo se importa un módulo, y su uso es bastante simple y elegante en comparación con los aros MIME por los que otros saltan. En su ejemplo, por el contrario, LibreOffice es más difícil de usar que el bloc de notas.
3pitt
4

Otras respuestas son excelentes, aunque todavía quería compartir un enfoque diferente en caso de que alguien esté buscando alternativas.

La principal diferencia aquí es que con este enfoque puede usar HTML / CSS para formatear su mensaje, de modo que pueda ser creativo y darle un poco de estilo a su correo electrónico. Aunque no está obligado a usar HTML, también puede usar solo texto sin formato.

Tenga en cuenta que esta función acepta enviar el correo electrónico a múltiples destinatarios y también permite adjuntar múltiples archivos.

Solo he intentado esto en Python 2, pero creo que también debería funcionar bien en 3:

import os.path
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication

def send_email(subject, message, from_email, to_email=[], attachment=[]):
    """
    :param subject: email subject
    :param message: Body content of the email (string), can be HTML/CSS or plain text
    :param from_email: Email address from where the email is sent
    :param to_email: List of email recipients, example: ["[email protected]", "[email protected]"]
    :param attachment: List of attachments, exmaple: ["file1.txt", "file2.txt"]
    """
    msg = MIMEMultipart()
    msg['Subject'] = subject
    msg['From'] = from_email
    msg['To'] = ", ".join(to_email)
    msg.attach(MIMEText(message, 'html'))

    for f in attachment:
        with open(f, 'rb') as a_file:
            basename = os.path.basename(f)
            part = MIMEApplication(a_file.read(), Name=basename)

        part['Content-Disposition'] = 'attachment; filename="%s"' % basename
        msg.attach(part)

    email = smtplib.SMTP('your-smtp-host-name.com')
    email.sendmail(from_email, to_email, msg.as_string())

¡Espero que esto ayude! :-)

Antony Fuentes Artavia
fuente
2
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib
import mimetypes
import email.mime.application

smtp_ssl_host = 'smtp.gmail.com'  # smtp.mail.yahoo.com
smtp_ssl_port = 465
s = smtplib.SMTP_SSL(smtp_ssl_host, smtp_ssl_port)
s.login(email_user, email_pass)


msg = MIMEMultipart()
msg['Subject'] = 'I have a picture'
msg['From'] = email_user
msg['To'] = email_user

txt = MIMEText('I just bought a new camera.')
msg.attach(txt)

filename = 'introduction-to-algorithms-3rd-edition-sep-2010.pdf' #path to file
fo=open(filename,'rb')
attach = email.mime.application.MIMEApplication(fo.read(),_subtype="pdf")
fo.close()
attach.add_header('Content-Disposition','attachment',filename=filename)
msg.attach(attach)
s.send_message(msg)
s.quit()

Para explicación, puede usar este enlace que explica correctamente https://medium.com/@sdoshi579/to-send-an-email-along-with-attachment-using-smtp-7852e77623

sdoshi
fuente
2
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
from email.mime.text import MIMEText
import smtplib

msg = MIMEMultipart()

password = "password"
msg['From'] = "from_address"
msg['To'] = "to_address"
msg['Subject'] = "Attached Photo"
msg.attach(MIMEImage(file("abc.jpg").read()))
file = "file path"
fp = open(file, 'rb')
img = MIMEImage(fp.read())
fp.close()
msg.attach(img)
server = smtplib.SMTP('smtp.gmail.com: 587')
server.starttls()
server.login(msg['From'], password)
server.sendmail(msg['From'], msg['To'], msg.as_string())
server.quit()
Abdul Haseeb
fuente
2
hola, bienvenido, siempre publique una explicación de su respuesta cuando responda una pregunta para una mejor comprensión
Ali
0

A continuación se muestra una combinación de lo que encontré en la publicación de SoccerPlayer Aquí y el siguiente enlace que me facilitó la tarea de adjuntar un archivo xlsx. Encontrado aquí

file = 'File.xlsx'
username=''
password=''
send_from = ''
send_to = 'recipient1 , recipient2'
Cc = 'recipient'
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = send_to
msg['Cc'] = Cc
msg['Date'] = formatdate(localtime = True)
msg['Subject'] = ''
server = smtplib.SMTP('smtp.gmail.com')
port = '587'
fp = open(file, 'rb')
part = MIMEBase('application','vnd.ms-excel')
part.set_payload(fp.read())
fp.close()
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment', filename='Name File Here')
msg.attach(part)
smtp = smtplib.SMTP('smtp.gmail.com')
smtp.ehlo()
smtp.starttls()
smtp.login(username,password)
smtp.sendmail(send_from, send_to.split(',') + msg['Cc'].split(','), msg.as_string())
smtp.quit()
TonyRyan
fuente
0

Con mi código puede enviar archivos adjuntos de correo electrónico utilizando gmail, deberá:

establezca su dirección de gmail en " SU CORREO ELECTRÓNICO SMTP AQUÍ "

configura la contraseña de tu cuenta de gmail en " TU CONTRASEÑA SMTP AQUÍ_ "

En la parte ___ CORREO PARA RECIBIR EL MENSAJE_ debe configurar la dirección de correo electrónico de destino.

La notificación de alarma es el tema,

Alguien ha entrado en la habitación, la imagen adjunta es el cuerpo

["/home/pi/webcam.jpg"] es un archivo adjunto de imagen.

#!/usr/bin/env python
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
import os

USERNAME = "___YOUR SMTP EMAIL HERE___"
PASSWORD = "__YOUR SMTP PASSWORD HERE___"

def sendMail(to, subject, text, files=[]):
    assert type(to)==list
    assert type(files)==list

    msg = MIMEMultipart()
    msg['From'] = USERNAME
    msg['To'] = COMMASPACE.join(to)
    msg['Date'] = formatdate(localtime=True)
    msg['Subject'] = subject

    msg.attach( MIMEText(text) )

    for file in files:
        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)

    server = smtplib.SMTP('smtp.gmail.com:587')
    server.ehlo_or_helo_if_needed()
    server.starttls()
    server.ehlo_or_helo_if_needed()
    server.login(USERNAME,PASSWORD)
    server.sendmail(USERNAME, to, msg.as_string())
    server.quit()

sendMail( ["___EMAIL TO RECEIVE THE MESSAGE__"],
        "Alarm notification",
        "Someone has entered the room, picture attached",
        ["/home/pi/webcam.jpg"] )
John Rua
fuente
¡Mucho tiempo sin verte! Es bueno ver que está atribuyendo correctamente su código e incluyéndolo directamente en la respuesta. Sin embargo, generalmente está mal visto copiar y pegar el mismo código de respuesta en múltiples preguntas. Si realmente se pueden resolver con la misma solución, debe marcar las preguntas como duplicadas .
Das_Geek
0

También puede especificar el tipo de archivo adjunto que desea en su correo electrónico, como ejemplo usé pdf:

def send_email_pdf_figs(path_to_pdf, subject, message, destination, password_path=None):
    ## credits: http://linuxcursor.com/python-programming/06-how-to-send-pdf-ppt-attachment-with-html-body-in-python-script
    from socket import gethostname
    #import email
    from email.mime.application import MIMEApplication
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    import smtplib
    import json

    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.starttls()
    with open(password_path) as f:
        config = json.load(f)
        server.login('[email protected]', config['password'])
        # Craft message (obj)
        msg = MIMEMultipart()

        message = f'{message}\nSend from Hostname: {gethostname()}'
        msg['Subject'] = subject
        msg['From'] = '[email protected]'
        msg['To'] = destination
        # Insert the text to the msg going by e-mail
        msg.attach(MIMEText(message, "plain"))
        # Attach the pdf to the msg going by e-mail
        with open(path_to_pdf, "rb") as f:
            #attach = email.mime.application.MIMEApplication(f.read(),_subtype="pdf")
            attach = MIMEApplication(f.read(),_subtype="pdf")
        attach.add_header('Content-Disposition','attachment',filename=str(path_to_pdf))
        msg.attach(attach)
        # send msg
        server.send_message(msg)

inspiraciones / créditos a: http://linuxcursor.com/python-programming/06-how-to-send-pdf-ppt-attachment-with-html-body-in-python-script

Pinocho
fuente