Python smtplib发送含内嵌图与附件邮件在Win10 Mail中附件不显示的解决方法
I’ve run into this exact issue before—Windows 10 Mail has a well-documented quirk when parsing certain nested MIME structures, especially when combining inline images and attachments. Your original setup works great for most clients, but Win10 Mail gets confused when attachments are siblings to a multipart/alternative block that contains a multipart/related sub-section.
The Root of the Problem
Your initial MIME hierarchy:
multipart/mixed multipart/alternative text/plain multipart/related text/html image/png (inline) application/pdf (attachment)
Win10 Mail appears to treat the entire multipart/alternative block as a single "content part" and ignores anything outside of the selected alternative (in this case, the multipart/related with your HTML and image), hence the missing attachment.
The Working Structure
To fix this, reverse the nesting of multipart/alternative and multipart/related, while keeping the attachment directly attached to the top-level multipart/mixed container. Here’s the correct hierarchy:
multipart/mixed multipart/related multipart/alternative text/plain text/html image/png (inline) application/pdf (attachment)
This structure makes it clear to Win10 Mail that:
- The
multipart/mixedholds two distinct parts: the message content (with inline image) and the attachment. - The
multipart/relatedgroups the HTML content with its inline image, ensuring proper linking. - The
multipart/alternativeprovides the plain text fallback for non-HTML clients.
Modified Python Code
Here’s how to adjust your code to implement this structure:
from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.image import MIMEImage from email.mime.application import MIMEApplication # Top-level container: holds content + attachments message = MIMEMultipart("mixed") message["From"] = "your-email@example.com" message["To"] = "recipient@example.com" message["Subject"] = "Test: Inline Image + Attachment" # Related container: groups alternative content with inline image related_container = MIMEMultipart("related") # Alternative container: plain text + HTML fallback alternative_container = MIMEMultipart("alternative") # Add plain text body plain_text = "Plain text fallback - check the attachment and inline image!" alternative_container.attach(MIMEText(plain_text, "plain")) # Add HTML body with inline image reference html_body = """ <html> <body> <p>HTML content with inline image:</p> <img src="cid:my-inline-image" alt="My Inline Image"> </body> </html> """ alternative_container.attach(MIMEText(html_body, "html")) # Attach alternative content to related container related_container.attach(alternative_container) # Add inline image to related container with open("inline-image.png", "rb") as img_file: image_content = img_file.read() inline_image = MIMEImage(image_content) inline_image.add_header("Content-Disposition", 'inline; filename="inline-image.png"') inline_image.add_header("Content-ID", "<my-inline-image>") # Matches cid in HTML related_container.attach(inline_image) # Attach the entire related content block to top-level mixed container message.attach(related_container) # Add the separate attachment with open("document.pdf", "rb") as attach_file: attach_content = attach_file.read() attachment = MIMEApplication(attach_content, Name="document.pdf") attachment.add_header("Content-Disposition", 'attachment; filename="document.pdf"') message.attach(attachment) # Send the email import smtplib with smtplib.SMTP_SSL("smtp.example.com", 465) as smtp_session: smtp_session.login("your-email@example.com", "your-password") smtp_session.sendmail(message["From"], message["To"], message.as_string())
Testing Results
This structure has been verified to work correctly in:
- Windows 10 Mail (shows both inline image and attachment)
- BlueMail (Android)
- iOS Mail
- Roundcube
The key change is moving the multipart/alternative inside the multipart/related block—this prevents Win10 Mail from truncating the attachment while maintaining compatibility with all other major clients.
内容的提问来源于stack exchange,提问作者Eli

