Search code examples
pythonemailmimesmtplib

SMTPlib Attachments not received


I've been using code based on the snippet at http://datamakessense.com/easy-scheduled-emailing-with-python-for-typical-bi-needs/ to send PDF attachments to customers through my company's email. We send about 100 of these at a time, through a single email address ("[email protected]"), and for each email sent, I send a BCC copy to an internal email address, as well ("[email protected]").

From time to time (about 5 out of 100), a customer reports not getting an attachment. Sometimes it doesn't show at all, and sometimes it shows with a red question mark. However, the BCC copy always has the attachment with no problems, and going into the sending account, the sent copy of the email always shows the attachment, also with no problem. There are no noticeable similarities in customers' emails who have not received the attachment (such as a shared domain; in fact, most are @gmail.com). There are no exceptions or errors to report. Everything looks as though it is properly working.

This is my first time working with MIME or automating emails through Python, but the fact that it is working 98% of the time is confusing me. Are there known reasons why this might be happening? Maybe I'm not setting the type correctly? Or is there anything special I should be doing with MIME for Gmail?

Here is my code:

wdir = 'PDFs\\'
filelist = []
for file in os.listdir(wdir):
    if file.endswith('.pdf'):
        filelist += [wdir + file]  # sending all of the PDFs in a local directory

email = {}
rf = wdir + 'Reports_data.csv'  # get email addresses for customers by ID (row[2])
with open(rf, 'rbU') as inf:
    read = csv.reader(inf)
    read.next()
    for row in read:
        email[row[2]] = row[3]

hfi = open('HTML\\email.html', 'rb')  # the HTML for the email body, itself
htmltxt = hfi.read()
hfi.close()


class Bimail:
    def __init__(self, subject, recipients):
        self.subject = subject
        self.recipients = recipients
        self.htmlbody = ''
        self.sender = "[email protected]"
        self.senderpass = 'password'
        self.attachments = []

    def send(self):
        msg = MIMEMultipart('alternative')
        msg['From'] = self.sender
        msg['Subject'] = self.subject
        msg['To'] = self.recipients[0]  
        msg.preamble = "preamble goes here"
        if self.attachments:
            self.attach(msg)
        msg.attach(MIMEText(self.htmlbody, 'html'))
        s = smtplib.SMTP('smtp.gmail.com:587')
        s.starttls()
        s.login(self.sender, self.senderpass)
        s.sendmail(self.sender, self.recipients, msg.as_string())
        s.quit()

    def htmladd(self, html):
        self.htmlbody = self.htmlbody + '<p></p>' + html

    def attach(self, msg):
        for f in self.attachments:    
            ctype, encoding = mimetypes.guess_type(f)
            if ctype is None or encoding is not None:
                ctype = "application/octet-stream"
            maintype, subtype = ctype.split("/", 1)
            fn = f.replace(wdir, '')
            fp = open(f, "rb")
            attachment = MIMEBase(maintype, subtype)
            attachment.set_payload(fp.read())
            fp.close()
            encoders.encode_base64(attachment)
            attachment.add_header("Content-Disposition", "attachment", filename=fn)
            attachment.add_header('Content-ID', '<{}>'.format(f))  # or should this be format(fn)?
            msg.attach(attachment)

    def addattach(self, files):
        self.attachments = self.attachments + files


if __name__ == '__main__':
    for fi in filelist:
        code = fi.split('_')[1].split('\\')[1]  # that "ID" for email is in the filename
        addr = email[code]
        mymail = Bimail(('SUBJECT HERE'), [addr, '[email protected]'])
        mymail.htmladd(htmltxt)
        mymail.addattach([fi])
        mymail.send()

Solution

  • Try out this block of code :

    import smtplib
    from email.MIMEMultipart import MIMEMultipart
    from email.MIMEText import MIMEText
    from email.MIMEBase import MIMEBase
    from email import encoders
    
    fromaddr = "[email protected]"
    password = "password"
    toaddr = "[email protected]"
    
    msg = MIMEMultipart()
    
    msg['From'] = fromaddr
    msg['To'] = toaddr
    msg['Subject'] = "Report"
    
    body = "Hi, have a look at the Report"
    
    msg.attach(MIMEText(body, 'plain'))
    
    filename = "Report.pdf"
    attachment = open("Report.pdf", "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.office365.com', 587)
    server.starttls()
    server.login(fromaddr, "password")
    text = msg.as_string()
    server.sendmail(fromaddr, toaddr, text)
    server.quit()
    

    It worked for me