
本文详解如何在 python 3.6+ 中使用 email 模块正确发送带真实类型(如 image/jpeg、application/pdf)和原始文件名的附件,避免附件统一被识别为 .eml 或 application/octet-stream,并替代已弃用的 set_payload() + mimebase 手动编码方式。
本文详解如何在 python 3.6+ 中使用 email 模块正确发送带真实类型(如 image/jpeg、application/pdf)和原始文件名的附件,避免附件统一被识别为 .eml 或 application/octet-stream,并替代已弃用的 set_payload() + mimebase 手动编码方式。
在 Python 3.6 及更高版本中,email.message.EmailMessage.add_attachment() 方法已大幅简化附件处理流程。它不仅自动完成 Base64 编码、设置标准 MIME 头部,还支持显式指定主类型(maintype)、子类型(subtype)和 filename,从而确保收件方邮件客户端能准确识别文件格式并显示正确扩展名(如 report.pdf 而非 unknown.bin 或 message.eml)。
关键改进在于:不再需要手动创建 MIMEBase 实例、调用 set_payload() 和 encoders.encode_base64() —— 这些操作在现代 email API 中已被标记为过时(set_payload() 在 MIMEPart 子类中已弃用),且易因编码/头部配置疏漏导致附件类型丢失。
以下为推荐的完整实践方案:
import mimetypes
from email.message import EmailMessage
from email.utils import make_msgid
import smtplib
def get_mime_type(filepath):
"""安全推断文件 MIME 类型;fallback 到 application/octet-stream"""
ctype, encoding = mimetypes.guess_type(filepath)
if ctype is None or encoding is not None:
ctype = 'application/octet-stream'
return ctype
# 构建邮件对象
msg = EmailMessage()
msg['Subject'] = '测试附件:图片与PDF'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('这是一封带附件的测试邮件。')
# 附件列表(支持多文件)
files = [
'/Users/Mine/Desktop/RKw.jpeg',
'/Users/Mine/Desktop/PG_2022.pdf',
]
for file_path in files:
try:
with open(file_path, 'rb') as f:
file_data = f.read()
# 自动推断 MIME 类型
ctype = get_mime_type(file_path)
maintype, subtype = ctype.split('/', 1)
# ✅ 推荐:直接 add_attachment(),传入原始字节、类型及文件名
msg.add_attachment(
file_data,
maintype=maintype,
subtype=subtype,
filename=file_path.split('/')[-1] # 仅取文件名,不含路径
)
print(f"✓ 已添加附件:{file_path.split('/')[-1]} ({ctype})")
except FileNotFoundError:
print(f"⚠ 文件未找到:{file_path}")
except Exception as e:
print(f"❌ 附件添加失败 {file_path}:{e}")
# 发送示例(需配置 SMTP)
# with smtplib.SMTP('smtp.example.com', 587) as server:
# server.starttls()
# server.login('user', 'password')
# server.send_message(msg)? 注意事项与最佳实践:
- 始终使用 with open(...) 上下文管理器:确保文件句柄及时释放,避免资源泄漏;
- mimetypes.guess_type() 并非 100% 可靠:对无扩展名或自定义格式文件可能返回 None,因此必须提供 application/octet-stream 回退逻辑;
- filename 参数必须是纯文件名(不含路径):若传入完整路径(如 /Users/.../RKw.jpeg),部分邮件客户端会将斜杠解析为目录结构,导致下载失败或重命名异常;
- 不要重复调用 set_content() 后再手动 attach():EmailMessage 设计为统一使用 add_attachment() 和 set_content(),混合旧式 MIMEMultipart 构造易引发头部冲突;
- 如需支持大文件:应改用流式读取(msg.add_attachment(f, ...) 直接传文件对象),并配合 Content-Transfer-Encoding: base64 的自动处理,无需额外编码。
通过上述方式,附件将严格按其真实类型传输,Outlook、Apple Mail、Gmail 等主流客户端均可正确渲染图标、识别扩展名,并允许用户一键保存为原始格式,显著提升邮件专业性与用户体验。










