Search code examples
pythoncsvemailmime-mail

Attach CSV to E-Mail with MIMEBase, only attaches 'bin' document


I want to write a function to attach a csv file to an E-Mail with HTML text. Everything works fine. I sends the E-Mail with the text and the attachment with the right information. Only the data format is wrong. I tried different varieties in MIMEBASE('application', 'octet-stream'). This one gives me a '.bin' data which I cannot open on macOS. Others gave me a plain text data which included the right data, but I don't wanna copy it manually into a csv. Does someone have a solution how I get out the data as '.csv'? Most of the solutions I found here just looked like my code below.

Code:

def send_mail(receiver, subject, filepath, attachname):
    
    #login information
    port = 123
    smtp_server = "test.server.com"
    login = "testlogin" # your login 
    password = "12345678910" # your password 

    # specify the sender’s and receiver’s email addresses and text in HTML
    sender = "[email protected]"
    receiver = receiver
    message = MIMEMultipart()
    message["Subject"] = subject
    message["From"] = sender
    message["To"] = COMMASPACE.join([receiver])
    html_text = """\
    <html>
      <body>
        <p>Hi ,<br>
        <p>kind regards</p>
        <p> This is an automatized Mail </p>
      </body>
    </html>
    """
    # convert both parts to MIMEText objects and add them to the MIMEMultipart message
    text = MIMEText(html_text, "html")
    message.attach(text)
    
    #add a file as attachment
    file = open(filepath, "rb")
    att = MIMEBase('application', 'octet-stream')
    att.set_payload(file.read())
    encoders.encode_base64(att)
    att.add_header("Content_Disposition",
                    'attachment', filename = attachname)
    message.attach(att)
    try:
        #send your message with credentials specified above
        with smtplib.SMTP(smtp_server, port) as server:
            server.connect(smtp_server, port)
            server.ehlo()
            server.starttls()
            server.ehlo()
            server.login(login, password)
            server.sendmail(sender, receiver, message.as_string())

        # tell the script to report if your message was sent or which errors need to be fixed 
        print('Sent')
    except (gaierror, ConnectionRefusedError):
        print('Failed to connect to the server. Bad connection settings?')
    except smtplib.SMTPServerDisconnected:
        print('Failed to connect to the server. Wrong user/password?')
    except smtplib.SMTPException as e:
        print('SMTP error occurred: ' + str(e))

send_mail('[email protected]', 'TEST', '/Users/Changer/Desktop/test.csv', 'test.csv')

Solution

  • Email clients may use the content type of the attachment part to determine which program is used to open the attachment, so specifying a suitable content type may help.

    This code uses the standard library's mimetypes module to try to guess the correct content type of the attachment, based on its name:

    import mimetypes
    
    mimetypes.init()
    
    def send_mail(receiver, subject, filepath, attachname):
        ...
    
        mimetype, _ = mimetypes.guess_type(attachname)
        if mimetype is None:
            mimetype = 'application/octet-stream'
        type_, _, subtype = mimetype.partition('/')
        att = MIMEBase(type_, subtype)
        ...
    

    The above code will generate these headers for the attachment:

    b'Content-Type: text/x-comma-separated-values'
    b'MIME-Version: 1.0'
    b'Content-Transfer-Encoding: base64'
    b'Content_Disposition: attachment; filename="test.csv"'
    

    Whether a mac will open a csv with a spreadsheet application (I assume this is what you want) depends on the configuration of the machine (See this blog for an example). If you want to be sure open the file in a spreadsheet it might be more effective to convert it to a spreadsheet format and send the spreadsheet.