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

如何解决Python通过SMTP发送.msg邮件时内嵌图片显示异常问题?

问题:.msg格式邮件通过SMTP发送后内嵌图片无法显示

我有一份.msg格式的新闻通讯邮件,使用Python基于SMTP协议发送后,邮件可正常接收,但内嵌图片无法加载,仅显示叉号(X)。查看邮件内容发现,HTML中的图片引用为<img src="cid:image001.png@01DB4FA6.D7B488E0" ... />这类格式,但图片资源无法匹配。

以下是我最初尝试的代码:

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
import extract_msg

# Function to extract email content and attachments from .msg file
def load_msg_file(file_path):
    msg = extract_msg.Message(file_path)
    
    # Extract components
    try:
        msg_body = msg.body or ""  # Plain text content
    except UnicodeDecodeError as e:
        print(f"Failed to decode plain text content: {e}")
        msg_body = ""
    
    try:
        msg_html = msg.htmlBody or b""  # HTML content as bytes
    except UnicodeDecodeError as e:
        print(f"Failed to decode HTML content: {e}")
        msg_html = b""

    # Decode HTML content to string
    if isinstance(msg_html, bytes):
        msg_html = msg_html.decode('utf-8', errors='ignore')

    msg_from = msg.sender
    msg_subject = msg.subject
    inline_images = {}

    # Extract attachments (specifically for inline images)
    for attachment in msg.attachments:
        file_name = attachment.longFilename or attachment.shortFilename
        if file_name and f'cid:{file_name}' in msg_html:
            # If the image filename is referenced in the HTML content
            inline_images[file_name] = attachment.data
            print(f"Found inline image: {file_name}")

    return msg_from, msg_subject, msg_body, msg_html, inline_images

# SMTP configuration
SMTP_HOST = "host"
SMTP_PORT = 587
SMTP_USERNAME = "username"
SMTP_PASSWORD = "password"

# Load the .msg file
msg_file_path = r"Newsletter.msg"
from_email, subject, plain_text_content, html_content, inline_images = load_msg_file(msg_file_path)

# Decode HTML content if necessary
if isinstance(html_content, bytes):
    html_content = html_content.decode('utf-8', errors='ignore')

# Ensure HTML contains references like &lt;img src="cid:image1.png"&gt;
print("HTML Content:\n", html_content)

# Recipient's email
to_email = "to@example.org"

# Create the email
email = MIMEMultipart("related")
email['From'] = "from@example.org"
email['To'] = to_email
email['Subject'] = subject

# Create alternative part for plain text and HTML
alt_part = MIMEMultipart("alternative")

if plain_text_content:
    alt_part.attach(MIMEText(plain_text_content, 'plain'))

if html_content:
    alt_part.attach(MIMEText(html_content, 'html'))

email.attach(alt_part)

# Attach inline images as embedded resources
for file_name, image_data in inline_images.items():
    img_part = MIMEImage(image_data)
    img_part.add_header('Content-ID', f"&lt;{file_name}&gt;")  # Set the Content-ID as 'cid:&lt;file_name&gt;'
    img_part.add_header('Content-Disposition', 'inline')
    email.attach(img_part)

# Send the email
try:
    with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
        server.starttls()
        server.login(SMTP_USERNAME, SMTP_PASSWORD)
        server.sendmail(from_email, to_email, email.as_string())
        print("Email sent successfully!")
except Exception as e:
    print(f"Failed to send email: {e}")

问题原因

原代码的核心问题是CID匹配错误:HTML中引用的CID是带后缀标识的完整值(如image001.png@01DB4FA6.D7B488E0),但代码中使用附件的文件名作为Content-ID,两者无法对应,导致邮件客户端找不到内嵌图片资源。


修正后的代码

修改load_msg_file函数,直接提取附件对应的原始CID,并以此作为Content-ID绑定图片资源:

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
import extract_msg

def load_msg_file(file_path):
    msg = extract_msg.Message(file_path)
    
    # 提取纯文本内容
    try:
        msg_body = msg.body or ""
    except UnicodeDecodeError as e:
        print(f"纯文本内容解码失败: {e}")
        msg_body = ""
    
    # 提取HTML内容并解码
    try:
        msg_html = msg.htmlBody or b""
    except UnicodeDecodeError as e:
        print(f"HTML内容解码失败: {e}")
        msg_html = b""
    if isinstance(msg_html, bytes):
        msg_html = msg_html.decode('utf-8', errors='ignore')

    msg_from = msg.sender
    msg_subject = msg.subject
    inline_images = {}

    # 提取内嵌图片:使用附件的原始CID匹配HTML引用
    for attachment in msg.attachments:
        # 获取附件的原始CID(extract_msg库的Attachment对象自带cid属性)
        cid = attachment.cid
        if cid and f'cid:{cid}' in msg_html:
            inline_images[cid] = attachment.data
            print(f"找到内嵌图片,CID: {cid}")

    return msg_from, msg_subject, msg_body, msg_html, inline_images

# SMTP配置
SMTP_HOST = "host"
SMTP_PORT = 587
SMTP_USERNAME = "username"
SMTP_PASSWORD = "password"

# 加载.msg文件
msg_file_path = r"Newsletter.msg"
from_email, subject, plain_text_content, html_content, inline_images = load_msg_file(msg_file_path)

# 收件人邮箱
to_email = "to@example.org"

# 构造邮件
email = MIMEMultipart("related")
email['From'] = "from@example.org"
email['To'] = to_email
email['Subject'] = subject

# 构造纯文本/HTML备选部分
alt_part = MIMEMultipart("alternative")
if plain_text_content:
    alt_part.attach(MIMEText(plain_text_content, 'plain'))
if html_content:
    alt_part.attach(MIMEText(html_content, 'html'))
email.attach(alt_part)

# 绑定内嵌图片:使用原始CID作为Content-ID
for cid, image_data in inline_images.items():
    img_part = MIMEImage(image_data)
    img_part.add_header('Content-ID', f"<{cid}>")
    img_part.add_header('Content-Disposition', 'inline')
    email.attach(img_part)

# 发送邮件
try:
    with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
        server.starttls()
        server.login(SMTP_USERNAME, SMTP_PASSWORD)
        server.sendmail(from_email, to_email, email.as_string())
        print("邮件发送成功!")
except Exception as e:
    print(f"邮件发送失败: {e}")

补充:.eml格式邮件的可行方案

对于.eml格式邮件,可直接解析原始邮件结构后发送,无需手动处理CID关联:

import smtplib
from email import policy
from email.parser import BytesParser
from email.utils import formatdate

# SMTP配置
smtp_host = "host"
smtp_port = 587
username = "username"
api_key = "key"

# .eml文件路径
eml_file_path = r"C:\mail.eml"

# 当前日期(用于替换邮件头)
current_date = formatdate(timeval=None, localtime=False, usegmt=True)

from_email = "from@example.com"
to_email = "to@example.com"

try:
    # 读取并解析.eml文件
    with open(eml_file_path, 'rb') as eml_file:
        msg = BytesParser(policy=policy.default).parse(eml_file)

    # 替换邮件头信息(按需修改)
    msg.replace_header('Date', current_date)
    msg.replace_header('From', from_email)
    msg.replace_header('To', to_email)

    # 发送邮件
    with smtplib.SMTP(smtp_host, smtp_port) as server:
        server.starttls()
        server.login(username, api_key)
        server.send_message(msg, from_addr=from_email, to_addrs=[to_email])
        print("邮件发送成功。")
        
except Exception as e:
    print(f"发送邮件出错: {e}")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 07:29:53