如何解决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 <img src="cid:image1.png"> 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"<{file_name}>") # Set the Content-ID as 'cid:<file_name>' 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-
相关产品推荐
相关产品推荐

