How to send email with pdf attachment in Python?
Stefan Bogdanescu
Founder & Senior Architect
How to Send Email with PDF Attachment in Python: A Complete Guide
Sending emails is a fundamental task in almost any application, whether you are building a simple script or a complex web service. When you need to attach files, especially PDFs, the process moves beyond simple text transmission and requires understanding how email protocols handle MIME (Multipurpose Internet Mail Extensions). As a senior developer, we need to ensure our solutions are not just functional but also secure and robust.
This guide will walk you through how to modify your basic Python smtplib code to successfully attach a local PDF file to an outgoing email.
Understanding the Challenge: Text vs. Binary Data
The initial code snippet you provided focuses solely on sending plain text (msg = 'Hello'). To include a PDF, we need to introduce the concept of multipart messages. An email is not just one string; it’s a composite structure containing headers, the body, and various attachments. Attachments, being binary files (like PDFs), must be encoded correctly so the receiving mail server can interpret them as actual files rather than corrupted text.
The solution lies in using Python's built-in email package, which handles the complex MIME encoding required for attachments seamlessly.
Step-by-Step Implementation with Python
To attach a file from your local system (e.g., /home/myuser/sample.pdf), you must perform three main steps: read the file in binary mode, create a MIME message object, and add the attachment part to that message.
1. Reading the PDF File
First, we need to open the PDF file in binary read mode ('rb'). This is crucial because attachments are raw bytes, not standard text.
2. Constructing the MIME Message
We will use MIMEMultipart to combine the email body and the attachment into a single message structure. The attachment itself will be added as a separate part.
3. Sending the Email via SMTP
Finally, we pass this structured MIME object to the sendmail function provided by smtplib.
Here is the complete, corrected code example:
import smtplib
from email importmime,mime.multipart
# --- Configuration ---
fromaddr = 'myemail@gmail.com'
toaddrs = 'youremail@gmail.com'
smtp_server = 'smtp.gmail.com'
smtp_port = 587
file_path = '/home/myuser/sample.pdf'
subject = 'Report with PDF Attachment'
message_body = 'Please find the requested document attached.'
# --- File Handling and MIME Construction ---
try:
# 1. Read the PDF file in binary mode
with open(file_path, 'rb') as f:
pdf_data = f.read()
# 2. Create the MIME message container
msg = mime.MIMEMultipart()
msg.clear() # Clear any default content
# Attach the body (text part)
msg.attach(mime.MIMEText(message_body, 'plain'))
# 3. Attach the PDF file as a separate part
# Use 'application/pdf' as the subtype for the attachment type
part = mime.datastructures.MIMEBase('application', 'pdf')
part.add_attachment(
filename='sample.pdf',
content=pdf_data,
type='application/pdf'
)
msg.attach(part)
# --- Sending the Email ---
print("Connecting to server...")
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls() # Secure the connection
server.login('myemail', 'yyyyyy') # Use environment variables for security!
# Send the fully constructed message
server.sendmail(fromaddr, toaddrs, msg.as_string())
server.quit()
print("Email sent successfully with PDF attachment!")
except FileNotFoundError:
print(f"Error: The file was not found at {file_path}")
except Exception as e:
print(f"An error occurred during email sending: {e}")
Security and Best Practices
As a senior developer, security must be paramount. Notice how in the example above, I explicitly noted that hardcoding credentials ('myemail', 'yyyyyy') is dangerous. In production environments—especially when dealing with services similar to those built in frameworks like Laravel where secure data handling is key—you should never store passwords directly in your code. Use environment variables or a dedicated secret management system to load these credentials dynamically.
Conclusion
Sending emails with file attachments requires moving beyond simple string manipulation and embracing the structure of MIME. By leveraging Python's email module, you can correctly package binary data into a standard email format. This approach ensures that your PDFs are transmitted reliably across various mail servers. While modern web development often focuses on PHP frameworks like Laravel for backend services, mastering these fundamental communication protocols in languages like Python is essential for building robust and scalable applications, regardless of the framework you choose to use.
Note: Blog content is currently available in English.