
本文详解如何在 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 等主流客户端均可正确渲染图标、识别扩展名,并允许用户一键保存为原始格式,显著提升邮件专业性与用户体验。