Search code examples
pythonemailsmtpmime

Is there a quick way to send email attachments in python using SMTP without knowing the file type in advance?


I'm working on a Python program that would send emails with attachments using SMTP. So far, searching the internet, I've come across two main ways to attach files to MIME emails. One way is to use the MIMEBase class and just attach the file like this (regardless of file type):

attach_file = open(attach_file_name, 'rb') # Open the file as binary mode
payload = MIMEBase('application', 'octate-stream')
payload.set_payload((attach_file).read())
encoders.encode_base64(payload) #encode the attachment
attach_file.close()
#add payload header with filename
payload.add_header('Content-Decomposition', "attachment; filename= %s" % attach_file_name)
msg.attach(payload)

The problem with this method is that I've found that on many email clients (e.g. Gmail) the attachment shows up as "noname" and is not previewable (though you can still view it by downloading and opening with the right program).

The second method I've come across is to attach the file with the sub-class corresponding to its type, like so:

f=open(attach_file_name, 'rb')
img_data = f.read()
f.close()
image = MIMEImage(img_data, name=os.path.basename(attach_file_name))
msg.attach(image)

This example would work well for an image and it would show up properly in Gmail, but it can't be used to send other types of files. If we want to send a file using method 2, it would appear that we need to know its type first. Is there a way to easily determine the type of a file as it concerns MIME such that the code can do this on the fly (without enumerating all different extensions for a certain file type)? Or is there a way to send an attachment and have it show up properly without knowing its type?


Solution

  • SMTP doesn't care what the attachment contains. Just put it as application/octet-stream and hope the recipient knows what to do with it. But make sure you spell the MIME type correctly. ("Octet" is just a fancy way to say "8-bit byte".)

    Also, Content-Decomposition is not a valid MIME header name; you want Content-Disposition.

    These horrible typos are probably why you observed problems in some clients. They can't guess what you mean when you misspell critical technical keywords.

    More fundamentally, you appear to have copy/pasted email code from several years back for both of your attempts. The current Python 3.6+ email library code is much more readable and versatile. Probably throw away what you have and start over with the examples in the documentation.

    If you can guess correctly the MIME type of the file you are transmitting, adding that information to the MIME headers would be useful; but automating this for arbitrary attachment types is unlikely to be helpful. If your guessing logic guesses wrong, you are just making things harder by claiming that you know when you don't.

    So, I don't particularly recommend even trying; but if you want to verify for yourself that this is futile, try the mimetypes.guess_type method from the standard library. The documentation suggests that this just contains canned guesses for a collection of file name extensions; a more thorough approach would be to use the actual contents of the file and libmagic or similar to attempt to identify what it is.