正确设置邮件附件的 MIME 类型与文件名

1次阅读

正确设置邮件附件的 MIME 类型与文件名

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

text=ZqhQzanResources