Nicklas A.
Nicklas A.

Reputation: 7061

Attachments don't show up in Outlook when sending via python

We're trying to send emails with attachments but for some reason the attachments don't show up for people using outlook.

If they forward the email to someone using gmail it shows up fine in gmail.
If a gmail user forwards the email to an outlook user it shows up in outlook (probably because gmail rebuilds the message).

This is the code we're using to send the email:

def send_email(headers={}, attachments=[], body={}):
    ADDRESS_HEADERS = set(['from', 'to', 'cc', 'bcc', 'reply-to'])
    msg = MIMEMultipart('alternative')
    msg.preamble = "You need a MIME-aware email client to read this email.\n"

    def add_headers():
        def encode_address(v):
            (name, address) = parseaddr(v)
            name = str(Header(unicode(name), 'utf-8'))
            address = address.encode('ascii')
            return formataddr((name, address))

        for key, value in headers.iteritems():
            if not isinstance(value, list):
                value = [value]
            if key.lower() in ADDRESS_HEADERS:
                value = map(encode_address, value)
            msg[key.title()] = u';'.join(value)

    def set_body():
        msg.attach(MIMEText(body.get('text', ''), 'plain', _charset='utf-8'))
        if 'html' in body:
            msg.attach(MIMEText(body['html'], 'html', _charset='utf-8'))

    def attach_file(attachment):
        maintype, subtype = attachment['mimetype'].split("/", 1)
        part = MIMEBase(maintype, subtype)
        filename = attachment['filename']
        name = attachment.get('name', os.path.basename(filename))
        with open(filename, 'rb') as f:
            part.set_payload(f.read())
        encoders.encode_base64(part)
        part.add_header('Content-Disposition', 'attachment', filename=name)
        msg.attach(part)

    add_headers()
    map(attach_file, attachments)
    set_body()
    composed = msg.as_string()

    p = subprocess.Popen("sendmail -t", shell=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
    (stdout, stderr) = p.communicate(composed)
    if p.returncode != 0:
        raise IOError(u'{}\n\n{}'.format(stdout, stderr).strip())

It's extremely hard to find any relevant information due to the fragmentation of email implementations.

The files we're attaching is excel files with the mime type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

I'm trying to find more information about which outlook version is being used.

Upvotes: 3

Views: 2392

Answers (3)

Mohammadreza Ghajar
Mohammadreza Ghajar

Reputation: 41

I had this problem too. You can solve it by changing following line

msg = MIMEMultipart('alternative') 

to

msg = MIMEMultipart("multipart")

Upvotes: 4

RasikhJ
RasikhJ

Reputation: 611

This is pretty straightforward when using the win32com.client

Once you create your SendEmail Object, it's pretty straightforward.

import win32com.client
outlook_obj = win32com.client.Dispatch ("Outlook Application")
sendEmail_obj = outlook_obj.CreateItem( 0x0 )

Create a list of strings, with each string being the Full Path to the file that you would like to attach to the current SendEmail Object ( i.e. "C:/User/SubFolder/Filename.pdf" ).

After adding relevant strings such as Recipient's Email Address, Subject, and Body like so :

sendEmail_obj.To ( "[email protected]" )
sendEmail_obj.Subject ( "Subject Title String" )
sendEmail_obj.Body ("Dear Friend:\n \t I wanted to...") 

Your list of Attachment Path string items should each represent the full filesystem path to the item that you would like to attach. Let's call this list of path strings our attachment_pathlist.

for CurrentAttachmentPath in attachment_pathlist : 
    sendEmail_obj.Attachments.Add ( CurrentAttachmentPath )

That should prepare all the attachments for send-off. And then all that's left is...

sendEmail_obj.Send()

Upvotes: 0

MikeHunter
MikeHunter

Reputation: 4304

Two things you can try. First, I've had better reliability with the mimetype set to standard bag-of-bits, whatever type you are really sending:

part = MIMEBase('application', "octet-stream")

Second, see if changing the attachment header like this helps:

part.add_header('Content-Disposition', 'attachment; filename="%s"' % name) 

Setting the header like this works for us sending to Outlook. This is using email version '4.0.3'. I don't know what version you are using. As you know, there are a bunch of them.

Upvotes: 2

Related Questions