Overview
This article explains how to create and send emails with attachments such as PDFs or images using the standard Python library email.message. We will show a versatile implementation that uses the mimetypes module to automatically detect the data format (MIME type) of the attached file.
Specifications (Input/Output)
- Input:
- Sender and recipient information, subject, and body text.
- The file path of the attachment.
- Output:
- Generation of an email message object containing the attachment.
- Execution of the email transfer via an SMTP server.
- Requirements:
- An available SMTP server environment (such as Gmail).
- The attachment file must exist and be readable.
Basic Usage
This is the basic procedure for reading a file in binary mode, identifying its MIME type, and adding it to the email object.
from email.message import EmailMessage
import mimetypes
msg = EmailMessage()
msg.set_content("Sending a file.")
file_path = "report.pdf"
# Guess the MIME type of the file
ctype, encoding = mimetypes.guess_type(file_path)
if ctype is None:
ctype = "application/octet-stream" # Default if unknown
maintype, subtype = ctype.split("/", 1)
# Read the file and attach it
with open(file_path, "rb") as f:
file_data = f.read()
msg.add_attachment(
file_data,
maintype=maintype,
subtype=subtype,
filename=f.name
)
Full Code
Below is a complete implementation example that creates an email with an attachment and sends it to a local SMTP server for testing. In production, change the SMTP host settings to your service provider (e.g., Gmail).
import smtplib
import mimetypes
import os
from email.message import EmailMessage
def send_email_with_attachment():
"""
Demo function to send an email with an attachment.
"""
# 1. Create email information
msg = EmailMessage()
msg["Subject"] = "[Daily Report] Sending Attachment"
msg["From"] = "myself@example.com"
msg["To"] = "boss@example.com"
msg.set_content("Hello.\nPlease find today's report attached.\nThank you.")
# 2. Process attachment
# Assume an image file exists in the same directory
target_file = "monthly_chart.png"
# Check if file exists to avoid errors
if not os.path.exists(target_file):
print(f"Error: File '{target_file}' not found.")
return
# Detect MIME type
mime_type, _ = mimetypes.guess_type(target_file)
if mime_type is None:
# Treat as general binary data if detection fails
mime_type = "application/octet-stream"
# Split into type/subtype (e.g., image/png -> main=image, sub=png)
main_type, sub_type = mime_type.split("/", 1)
try:
with open(target_file, "rb") as f:
file_data = f.read()
file_name = os.path.basename(target_file)
# Add attachment data to the message
msg.add_attachment(
file_data,
maintype=main_type,
subtype=sub_type,
filename=file_name
)
print(f"Attached file: {file_name} ({mime_type})")
# 3. SMTP Transfer
# Using localhost:1025 for a test server
# Use smtp.gmail.com:587 for production
smtp_host = "localhost"
smtp_port = 1025
print("Sending email...")
with smtplib.SMTP(smtp_host, smtp_port) as smtp:
# smtp.starttls() # Enable if necessary
# smtp.login("user", "pass") # Enable if necessary
smtp.send_message(msg)
print("Sent successfully.")
except Exception as e:
print(f"An error occurred during sending: {e}")
if __name__ == "__main__":
# Create a test file for demonstration
with open("monthly_chart.png", "wb") as f:
f.write(b"Dummy Image Data")
send_email_with_attachment()
# Clean up
os.remove("monthly_chart.png")
Customization Points
Specifications for the main methods and modules related to attachment processing.
MIME Type Detection (mimetypes)
| Syntax | Description |
mimetypes.guess_type(path) | Returns a tuple (type, encoding) based on the file extension. Example: 'document.pdf' -> ('application/pdf', None) |
Adding Attachments (EmailMessage)
| Parameter | Description |
data (1st arg) | The actual binary data of the file (the return value of f.read()). |
maintype | The main category of the MIME type (e.g., image, application, text). |
subtype | The subcategory of the MIME type (e.g., jpeg, pdf, plain). |
filename | The filename displayed to the recipient. |
Explanation
- Benefits of
mimetypes: You do not need to write complex logic likeif ext == '.jpg': ...for every extension. This makes your code more general and clean. - application/octet-stream: This is a general type indicating “arbitrary binary data.” If an extension cannot be identified, using this ensures the file is at least treated as a downloadable attachment by the recipient’s client.
Important Notes
- File Size Limits: Most email servers (like Gmail) have a total size limit (e.g., 25MB) for the entire email including attachments. If you send videos or high-resolution images, the server may reject the email even if your Python code is correct.
- File Path vs. Filename: It is proper etiquette to pass only the filename (using
os.path.basename) to thefilenameargument instead of the full path. Passing a full path may trigger security warnings or display incorrectly in the recipient’s environment. - Binary Mode: Always use
rb(Read Binary) mode in theopen()function when handling non-text files like images or PDFs.
Advanced Usage
Example of a loop to attach all files in a folder at once.
import os
import mimetypes
from email.message import EmailMessage
def attach_all_files_in_dir(msg, directory):
"""
Attaches all files within a specified directory to the email.
"""
for filename in os.listdir(directory):
path = os.path.join(directory, filename)
# Skip directories
if not os.path.isfile(path):
continue
# Detect MIME type
ctype, encoding = mimetypes.guess_type(path)
if ctype is None or encoding is not None:
# Use general type if unknown or compressed
ctype = "application/octet-stream"
maintype, subtype = ctype.split("/", 1)
with open(path, "rb") as f:
file_data = f.read()
msg.add_attachment(
file_data,
maintype=maintype,
subtype=subtype,
filename=filename
)
print(f"Added: {filename}")
# Example Use:
# msg = EmailMessage() ...
# attach_all_files_in_dir(msg, "./documents")
Summary
By using the add_attachment method of the EmailMessage class, you can attach files without worrying about complex MIME structures. Combine it with the mimetypes module to automatically set the correct file format and build a smart email delivery program.
