在Python中,如何在不重命名的情况下发送具有完全相同文件名的多个电子邮件附件?

编程语言 2026-07-09

我在使用Python的 email.message.EmailMessageaiosmtplib 发送自动邮件。我的要求是附带多个具有 完全相同的文件名 的文件(来自不同来源的文件夹)。

问题:
尽管我在循环中多次调用 add_attachment(),收件人(在Gmail中查看)只看到一个附件。似乎Gmail将第一个文件覆盖为第二个,因为它们有相同的文件名。

我的代码片段:

from email.message import EmailMessage
import aiosmtplib

msg = EmailMessage()
msg["Subject"] = "Reports"
msg["From"] = "[email protected]"
msg["To"] = "[email protected]"

# Example of how I attach files
files_to_attach = [
    {"path": "folder1/report.pdf", "name": "report.pdf"},
    {"path": "folder2/report.pdf", "name": "report.pdf"}
]

for file in files_to_attach:
    with open(file["path"], 'rb') as f:
        msg.add_attachment(
            f.read(),
            maintype='application',
            subtype='octet-stream',
            filename=file["name"]
        )

# Sending via aiosmtplib
# await aiosmtplib.send(msg, ...)

提问:
有没有办法在不修改 filename 属性的前提下,强制像Gmail这样的电子邮件客户端显示两个附件?是否有可行的MIME头字段或“隐藏字符”技巧?

解决方案

我找到一个解决方法,既满足不重命名文件的约束,又能强制Gmail显示两个附件。

问题在于Gmail的界面会合并或隐藏MIME部分的 filename 参数中具有完全相同字符串的附件。为绕过此问题,我在每个后续重复的文件名末尾添加了一个 不可断行空格 (\xa0)

为什么这样有效:

  1. 对Gmail:report.pdfreport.pdf\xa0 从严格意义上来说是不同的字符串,因此会将它们渲染为两个独立的附件。
  2. 对用户:\xa0 字符在大多数邮件客户端(包括Gmail的网页端和移动端界面)中是不可见的,因此收件人看到的文件名会看起来相同。
  3. 下载时:用户下载文件时,操作系统会自动处理重复命名(例如在末尾添加 (1))。

修改后的代码

import asyncio
import os
from email.message import EmailMessage
import aiosmtplib


async def send_mail_with_duplicate_filenames():
    # 1. Email Setup
    msg = EmailMessage()
    msg["Subject"] = "Reports with Same Filenames"
    msg["From"] = "[email protected]"  
    msg["To"] = "[email protected]"      d
    msg.set_content("Hi, please find the attached reports. Note: Some files have identical names.")

    # 2. Files to attach (Example: different paths, same display name)
    # Note: Make sure these files exist in your local folders
    files_to_attach = [
        {"path": "folder1/report.pdf", "name": "report.pdf"},
        {"path": "folder2/report.pdf", "name": "report.pdf"},
        {"path": "folder3/report.pdf", "name": "report.pdf"}
    ]

    # Dictionary to keep track of filename occurrences for the hidden space trick
    name_tracker = {}

    for file_info in files_to_attach:
        file_path = file_info["path"]
        original_name = file_info["name"]

        if os.path.exists(file_path):
            # Track how many times this name has been used
            count = name_tracker.get(original_name, 0)
            name_tracker[original_name] = count + 1

            # TRICK: Add hidden non-breaking spaces (\xa0) for duplicates
            # 1st file: "report.pdf"
            # 2nd file: "report.pdf\xa0"
            # 3rd file: "report.pdf\xa0\xa0"
            display_name = original_name + ("\xa0" * count)

            with open(file_path, 'rb') as f:
                msg.add_attachment(
                    f.read(),
                    maintype='application',
                    subtype='octet-stream',
                    filename=display_name
                )
            print(f"Successfully attached: {display_name} from {file_path}")
        else:
            print(f"Warning: File not found at {file_path}")

    # 3. Sending the Mail
    # Note: Use 'App Password' if you are using Gmail
    SMTP_SERVER = "smtp.gmail.com"
    SMTP_PORT = 465
    SENDER_EMAIL = "[email protected]"
    SENDER_PASSWORD = "your_app_password" # Gmail App Password

    try:
        print("Sending email...")
        await aiosmtplib.send(
            msg,
            hostname=SMTP_SERVER,
            port=SMTP_PORT,
            use_tls=True,
            username=SENDER_EMAIL,
            password=SENDER_PASSWORD
        )
        print("Email sent successfully! Gmail will now show all attachments.")
    except Exception as e:
        print(f"Error occurred: {e}")

# Run the async function
if __name__ == "__main__":
    asyncio.run(send_mail_with_duplicate_filenames())
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章