0
votes

I am trying to send out emails with attachments to various recipients stored in a csv file. My aim is to send the recipients the corresponding attachment which is stored in a specific location.

The csv looks like this

Name,Emails,Firm
Tom,[email protected],Firm1
Dick,[email protected],Firm2
Harry,[email protected],Firm3

Within the folder the attachments would look like this

path='C:\\Documents\\Firms'

Within the location the files would look as follows

Firm1.xlsx
Firm2.xlsx
Firm3.xlsx

The following code uses the name and email in the csv to send out a personalised email while going through the folder to attach the corresponding excel file.

import email, csv, smtplib, ssl, fnmatch, os, codecs

from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import fnmatch

path = 'C:\\Documents\\Firms'
email_html = open('email.html')
body = email_html.read()


from_address = "[email protected]"
password = input("Type your password and press enter: ")

#context = ssl.create_default_context()
with smtplib.SMTP("smtp.office365.com", 587) as server:
    server.ehlo()
    server.starttls()
    server.login(from_address, password)
    with open("Firms.csv") as file:
        reader = csv.reader(file)
        next(reader)
        for Name, Email, Firm in reader:
            # Create a multipart message and set headers
            message = MIMEMultipart()
            message["From"] = from_address
            message["To"] = Email
            message["Subject"] = 'Test ' +  Name

            #Add body to email and excel attachment

            message.attach(MIMEText(body, "html"))

            for i in os.listdir(path):
                if os.path.isfile(os.path.join(path,i)) and Firm in i:
                    with open(os.path.join(path, i), 'rt', encoding='ISO-8859-1') as attachment:
                        part = MIMEBase("application", "octet-stream")
                        part.set_payload(attachment.read())

                        encoders.encode_base64(part)

                        part.add_header(
                        "Content-Disposition",f"attachment; filename= {Firm}",)

                    message.attach(part)
                    text = message.as_string()

                    # Use server to send email
                    server.sendmail(from_address, Email, text.format(name=Name))

Although this sends out emails the attachments are not in an excel format. When I used a encoding='utf8' I would get following error

UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position 18: character maps to <undefined>

Any help here would be much appreciated.

1
is this occur during file read opertaion - Hari
Yes, it is. It gives a traceback error once it arrives here: part.set_payload(attachment.read()) - Mr.C
try using 'rU' file mode with encoding utf8 - Hari
Still get the same error message unfortunately. - Mr.C
try using rb mode - Hari

1 Answers

0
votes

First, make sure to import base64. Then, try reading the file in binary mode. Specifically,

with open(os.path.join(path, i), 'rb') as attachment:

Then, you can read the contents of the file, encode it in base64, and then set this is as payload of the part, like:

part.set_payload(base64.b64encode(attachment.read()))

Of course remove the next line (encoders.encode_base64(part)), I think encoders is deprecated anyway.

I believe this should do the trick.