You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python通过SMTP发送内嵌图片邮件时出现AttributeError: policy错误

错误原因
  • 直接将PIL库的Image对象作为邮件部件附加到MIMEMultipart对象中:email库要求attach()方法传入的必须是符合MIME规范的部件对象(即email.mime下的各类MIME实例),PIL Image不属于合法MIME部件,因此邮件序列化时尝试访问MIME对象专属的policy属性就会抛出你看到的AttributeError: policy报错。
  • 未做内嵌图片与HTML正文的关联绑定:即使将图片转为合法MIME部件,如果没有给图片设置唯一Content-ID,也没有在HTML正文中通过cid协议引用对应ID的图片,图片要么不会显示,要么会被识别为普通附件,无法实现在正文内嵌展示的效果。
修正方案
  1. 将所有待内嵌的图片转为MIMEImage类型的合法MIME部件,不要直接传入PIL Image对象。
  2. 为每个内嵌图片设置全局唯一的Content-ID头,同时在HTML正文的<img>标签中用cid:对应ID作为src属性值,建立正文和图片的关联。
  3. 给图片部件添加Content-Disposition: inline头,明确标记为内嵌资源,避免邮件客户端将其识别为可下载附件。
  4. 保持邮件根容器为multipart/related类型,该类型是专门为携带正文关联资源(如内嵌图片、样式)设计的,不要换成multipart/mixed。
修正后可直接运行的代码
from email.mime.image import MIMEImage
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib
from io import BytesIO
from PIL import Image

# 初始化邮件容器
msg = MIMEMultipart("related")
msg['Subject'] = subject
msg['From'] = from_addr
msg['To'] = to_addr

# 第一步:生成带图片引用的HTML正文
html_body = """
<html>
  <body>
    <p>邮件正文内容:</p>
"""
cid_mapping = {}
for img_idx, image_name in enumerate(plots.keys()):
    current_cid = f"embed_img_{img_idx}"
    cid_mapping[image_name] = current_cid
    # 在HTML中插入图片标签,通过cid引用对应资源
    html_body += f'<img src="cid:{current_cid}" alt="{image_name}" style="max-width: 100%;"><br>'
html_body += """
  </body>
</html>
"""
# 挂载HTML正文部件
msg.attach(MIMEText(html_body, "html", "utf-8"))

# 第二步:挂载所有内嵌图片部件
for image_name, image_binary in plots.items():
    # 如果不需要对图片做压缩、裁剪、格式转换,直接用原始二进制流即可,无需PIL处理
    # 若需要用PIL处理图片,解开下方注释代码
    # pil_img = Image.open(BytesIO(image_binary))
    # img_buffer = BytesIO()
    # pil_img.save(img_buffer, format="PNG") # 根据实际图片格式调整
    # img_buffer.seek(0)
    # mime_img = MIMEImage(img_buffer.read())
    
    # 直接使用原始图片二进制的写法
    mime_img = MIMEImage(image_binary)
    
    # 绑定Content-ID,注意值必须用尖括号包裹
    mime_img.add_header('Content-ID', f'<{cid_mapping[image_name]}>')
    # 标记为内嵌资源
    mime_img.add_header('Content-Disposition', 'inline', filename=image_name)
    msg.attach(mime_img)

# 发送邮件
with smtplib.SMTP(host) as smtp_conn:
    # 若SMTP服务需要TLS加密、账号鉴权,解开下方对应代码
    # smtp_conn.starttls()
    # smtp_conn.login(from_addr, email_password)
    smtp_conn.sendmail(from_addr, to_addr, msg.as_string())
注意事项
  • 若无需对图片做二次处理,不要额外用PIL打开再保存,直接传入原始图片二进制可以减少不必要的性能损耗。
  • Content-ID头的值必须用尖括号包裹,HTML中引用cid时不需要加尖括号,直接写cid:id值即可。
  • 如果发送后图片还是被识别为附件,优先检查邮件容器类型是否为multipart/related,以及Content-Disposition是否设置为inline。

内容的提问来源于stack exchange,提问作者Mikee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 17:27:26