Question

The following Python function results in the attachment being named "noname" when it should be "text_file.txt". As you can see I've tried a 2 different approaches with MIMEBase and MIMEApplication. I've also tried MIMEMultipart('alternative') to no avail.

def send_email(from_addr, to_addr_list,
              subject, html_body,plain_text_body,
              login,
              password,
              smtpserver='smtp.gmail.com:587',
              cc_addr_list=None,
              attachment=None,
              from_name=None):

    message=MIMEMultipart()

    plain=MIMEText(plain_text_body,'plain')
    html=MIMEText(html_body,'html') 

    message.add_header('from',from_name)
    message.add_header('to',','.join(to_addr_list))
    message.add_header('subject',subject)

    if attachment!=None:
        #attach_file=MIMEBase('application',"octet-stream")
        #attach_file.set_payload(open(attachment,"rb").read())
        #Encoders.encode_base64(attach_file)
        #f.close()
        attach_file=MIMEApplication(open(attachment,"rb").read())
        message.add_header('Content-Disposition','attachment; filename="%s"' % attachment)
        message.attach(attach_file)


    message.attach(plain)
    message.attach(html)

    server = smtplib.SMTP(smtpserver)
    server.starttls()
    server.login(login,password)
    server.sendmail(from_addr, to_addr_list, message.as_string())
    server.quit()

How I'm calling the function:

send_email(
           from_addr=from_email,
           to_addr_list=["some_address@gmail.com"],
           subject=subject,
           html_body=html,
           plain_text_body=plain,
           login=login,
           password=password,
           from_name=display_name,
           attachment="text_file.txt"
           )
Was it helpful?

Solution

Your header isn't correct. filename is the attribute not a string.

# Add header to variable with attachment file
attach_file.add_header('Content-Disposition', 'attachment', filename=attachment)
# Then attach to message attachment file    
message.attach(attach_file)

OTHER TIPS

Old:

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

Update to:

message.add_header('content-disposition', 'attachment',
                   filename='%s' % 'your_file_name_only.txt' )

I think that this might not be relevant but for those who are interested and getting the same problem :

I am using the google API example too (the one without the attachment) and I realised that the attachment is put only when the text in the subject or the body is NOT a complete string i.e. the string which is being put in the subject or the body is not a single string but an collection of strings.

To explain better :

message = (service.users().messages().send(userId='me', body=body).execute())
body = ("Your OTP is", OTP)

This (body = ("Your OTP is", OTP)) may work for print() command, but it doesn't work for this case. You can change this :

message = (service.users().messages().send(userId='me', body=body).execute())
body = ("Your OTP is", OTP)

to :

CompleteString = "Your OTP is " + OTP
message = (service.users().messages().send(userId='me', body=body).execute())
body = (CompleteString)

The above lines make the 2 parts of the body into a single string.

Also : The 'noname' file that is put as an attachment contains only the written string. So, if you follow this :

message = (service.users().messages().send(userId='me', body=body).execute())
body = ("Your OTP is", OTP)

So all you'll get in the file will be : "Your OTP is "

I am also adding the entire code that I got after modifying the already existing sample code here : https://developers.google.com/gmail/api/quickstart/python

from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from email.mime.text import MIMEText
import base64

sender = "sender_mail"

print("Welcome to the Mail Service!")
reciever = input("Please enter whom you want to send the mail to - ")
subject = input("Please write your subject - ")
msg = input("Please enter the main body of your mail - ")

SCOPES = ['https://www.googleapis.com/auth/gmail.modify']
creds = None
if os.path.exists('token.pickle'):
    with open('token.pickle', 'rb') as token:
        creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
        creds.refresh(Request())
    else:
        flow = InstalledAppFlow.from_client_secrets_file(
            'credentials.json', SCOPES)
        creds = flow.run_local_server(port=0)
    # Save the credentials for the next run
    with open('token.pickle', 'wb') as token:
        pickle.dump(creds, token)
service = build('gmail', 'v1', credentials=creds)
message = MIMEText(msg)
message['to'] = reciever
message['from'] = sender
message['subject'] = subject
raw = base64.urlsafe_b64encode(message.as_bytes())
raw = raw.decode()
body = {'raw' : raw}
message = (service.users().messages().send(userId='me', body=body).execute())

Please also note that this code only works for text that is put through the mail.

P.S. I am using Python 3.8 so the above code might not work for Python 2.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top