Comment envoyer des pièces jointes par e-mail?

283

J'ai des problèmes pour comprendre comment envoyer une pièce jointe par e-mail à l'aide de Python. J'ai envoyé avec succès des messages simples avec le smtplib. Quelqu'un pourrait-il expliquer comment envoyer une pièce jointe dans un e-mail? Je sais qu'il y a d'autres articles en ligne mais en tant que débutant Python, je les trouve difficiles à comprendre.

Richard
la source
5
voici une implémentation simple qui peut attacher plusieurs fichiers, et même y faire référence dans le cas d'images à intégrer. datamakessense.com/…
AdrianBR
J'ai trouvé que cet utilitaire drupal.org/project/mimemail/issues/911612 s'avère que les pièces jointes doivent être attachées à une partie enfant de type connexe. Si vous joignez l'image à la partie MIME racine, les images peuvent apparaître dans la liste des éléments joints et prévisualisées dans des clients comme outlook365.
Hinchy
@AdrianBR et si j'ai une image au format PDF. Les Png ont des problèmes de pixels lors du zoom, donc les png ne sont pas bons pour moi.
Pinocchio

Réponses:

416

En voici une autre:

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

C'est à peu près la même chose que le premier exemple ... Mais il devrait être plus facile de passer.

Oli
la source
7
Soyez prudent avec les valeurs par défaut modifiables: stackoverflow.com/questions/101268/hidden-features-of-python/…
Gringo Suave
11
@ user589983 Pourquoi ne pas suggérer une modification comme n'importe quel autre utilisateur ici? J'ai changé la référence restante fileen f.
Oli
9
Avis pour les développeurs python3: module « email.Utils » a été renommé « email.utils »
Gecco
7
pour python2.5 +, il est plus facile d'utiliser MIMEApplication à la place - réduit les trois premières lignes de la boucle à:part = MIMEApplication(open(f, 'rb').read())
mata
5
Le sujet n'a pas été indiqué dans l'e-mail envoyé. Fonctionné uniquement après avoir changé la ligne en msg ['Subject'] = subject. J'utilise python 2.7.
Luke
70

Voici la version modifiée de Olipour python 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
la source
merci mais ce serait bien d'avoir aussi la base: la syntaxe pour un seul fichier joint (en utilisant son chemin)
JinSnow
semble que vous ne fermez pas vos fichiers, ils seront récupérés ou fermés à la sortie mais c'est une mauvaise habitude. avec open () comme f: est la bonne façon.
comte
Le code ne fonctionne pas. Le nom de variable f incorrect au format (os.path.basename (f)) doit être au format (os.path.basename (path))
Chris
1
send_to devrait être la liste [str]
Subin
2
meilleure réponse pour moi mais il y a une petite erreur: remplacer import pathlibparfrom pathlib import Path
AleAve81
65

c'est le code que j'ai fini par utiliser:

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

Le code est à peu près le même que celui d'Oli. Merci a tous

Code basé sur le message de problème de pièce jointe au fichier binaire .

Richard
la source
2
Bonne réponse. Ce serait bien s'il contenait également du code qui ajoute un exemple de corps de texte.
Steven Bluen
4
Veuillez noter que dans les versions modernes de la bibliothèque de messagerie - les importations de modules sont différentes. par exemple: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()

Adapté d' ici .

Oli
la source
Pas tout à fait ce que je recherche. Le fichier a été envoyé dans le corps d'un e-mail. Il manque également des crochets aux lignes 6 et 7. Je pense que nous nous rapprochons cependant
Richard
2
Les e-mails sont en texte brut, et c'est ce qui smtplibprend en charge. Pour envoyer des pièces jointes, vous les codez en tant que message MIME et les envoyez dans un e-mail en texte brut. Il y a cependant un nouveau module de messagerie python: docs.python.org/library/email.mime.html
Katriel
@katrienlalex un exemple de travail contribuerait grandement à ma compréhension
Richard
1
Êtes-vous sûr que l'exemple ci-dessus ne fonctionne pas? Je n'ai pas de serveur SMTP à portée de main, mais j'ai regardé msg.as_string()et il ressemble certainement au corps d'un e-mail multipartie MIME. Wikipedia explique 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

Une autre façon avec python 3 (si quelqu'un cherche):

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

Assurez-vous d'autoriser les « applications moins sécurisées » sur votre compte Gmail

Sudarshan
la source
6

Version Gmail, fonctionnant avec Python 3.6 (notez que vous devrez modifier vos paramètres Gmail pour pouvoir envoyer des e-mails via smtp à partir de celui-ci:

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

Usage:

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

Pour l'utiliser avec n'importe quel autre fournisseur de messagerie, modifiez simplement les configurations smtp.

Ferrarezi
la source
4

Le code le plus simple auquel j'ai pu accéder est:

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

Il était basé sur la documentation officielle de Django

Andrade
la source
3
dans votre cas vous devez installer django pour envoyer un email ... il ne répond pas correctement à la question
comte
@comte 'coz python n'est utilisé que pour Django, non?
Auspex
5
@Auspex c'est mon point ;-) c'est comme installer LibreOffice pour éditer un fichier de configuration ...
comte
Je trouve cela utile et instructif. seul un module est importé, et son utilisation est assez simple et élégante par rapport aux cerceaux MIME que d'autres traversent. Dans votre exemple, en revanche, LibreOffice est plus difficile à utiliser que le bloc-notes.
3pitt
4

Les autres réponses sont excellentes, même si je voulais toujours partager une approche différente au cas où quelqu'un chercherait des alternatives.

La principale différence ici est qu'en utilisant cette approche, vous pouvez utiliser HTML / CSS pour formater votre message, afin que vous puissiez faire preuve de créativité et donner du style à votre e-mail. Bien que vous ne soyez pas obligé d'utiliser HTML, vous pouvez également utiliser uniquement du texte brut.

Notez que cette fonction accepte d'envoyer l'e-mail à plusieurs destinataires et permet également de joindre plusieurs fichiers.

J'ai seulement essayé cela sur Python 2, mais je pense que cela devrait bien fonctionner sur 3 également:

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

J'espère que ça aide! :-)

Antony Fuentes Artavia
la source
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()

Pour explication, vous pouvez utiliser ce lien qu'il explique correctement https://medium.com/@sdoshi579/to-send-an-email-along-with-attachment-using-smtp-7852e77623

sdoshi
la source
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
la source
2
salut, bienvenue, veuillez toujours poster une explication de votre réponse lorsque vous répondez à une question pour une meilleure compréhension
Ali
0

Vous trouverez ci-dessous une combinaison de ce que j'ai trouvé dans le post de SoccerPlayer ici et du lien suivant qui m'a permis de joindre plus facilement un fichier xlsx. Trouvé ici

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
la source
0

Avec mon code, vous pouvez envoyer des pièces jointes par e-mail à l'aide de gmail, vous devrez:

définissez votre adresse gmail sur " VOTRE E-MAIL SMTP ICI "

définissez le mot de passe de votre compte gmail sur " VOTRE MOT DE PASSE SMTP ICI_ "

Dans la partie ___EMAIL POUR RECEVOIR LE MESSAGE_, vous devez définir l'adresse e-mail de destination.

La notification d'alarme est le sujet,

Quelqu'un est entré dans la pièce, l'image ci-jointe est le corps

["/home/pi/webcam.jpg"] est une pièce jointe d'image.

#!/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
la source
Ça fait longtemps! C'est bien de voir que vous attribuez correctement votre code et que vous l'incluez directement dans la réponse. Cependant, il est généralement mal vu de copier-coller le même code de réponse sur plusieurs questions. S'ils peuvent vraiment être résolus avec la même solution, vous devez plutôt signaler les questions comme des doublons .
Das_Geek
0

Vous pouvez également spécifier le type de pièce jointe que vous souhaitez dans votre e-mail, par exemple, j'ai utilisé le 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)

inspirations / crédits à: http://linuxcursor.com/python-programming/06-how-to-send-pdf-ppt-attachment-with-html-body-in-python-script

Pinocchio
la source