The comment is wrong; you are not opening the file in binary mode ('rb'
) and so you are basically corrupting it
Following up on my answer to your previous question, here is a refactored version of your code to use the modern, Python 3.6+ EmailMessage
library instead.
from email.message import EmailMessage
...
message = EmailMessage()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject
message.set_content(body, 'plain')
# The OS will look in the current working directory, not in the directory where you saved the script
filename = "file_name.xlsx"
# Notice 'rb'
with open(filename, 'rb') as attachment:
message.add_attachment(
attachment.read(),
maintype='application', subtype='octet-stream')
# AGAIN, no need for a context if you are just using the default SSL
with smtplib.SMTP_SSL(smtp_server, 465) as server:
server.login(sender_email, password)
# AGAIN, prefer the modern send_message method
server.send_message(message)
As you notice, there's quite a lot less boilerplate than with the older Email.Message
API from Python 3.5 or earlier.
A common arrangement is to have a multipart/related
message with body text and an attachment, but the body text represented as a multipart/alternative
structure with both a text/plain
and a text/html
rendering of the message. If you want this, the "asparagus" example from the email
module documentation's examples page contains a recipe which demonstrates that (though it also includes an image instead of a binary attachment, and displays it inline in the HTML attachment).
Tangentially, you might be better off choosing a less clunky format than Excel for sharing data. If it's just a table, a CSV text file would be a lot smaller as well as easier to process on the receiving end. (If you do send Excel, probably actually use its designated MIME type rather than the generic application/octet-stream
.)