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

Python提取.msg文件附件并添加至MIME邮件发送的实现及报错解决求助

解决.msg附件提取并转发的错误与优化方案

先拆解你遇到的问题根源:

  • 核心错误是文件句柄未关闭:你打开临时文件后没有释放句柄,导致临时目录自动清理时,系统无法删除被占用的文件,进而触发后续的NotADirectoryError(清理流程误将文件识别为目录)。
  • 隐藏问题是重复复用同一个MIMEBase对象:循环里始终用同一个part变量,会导致所有附件最终都是最后一个附件的内容,同时HTTP头也会混乱。
  • 额外优化点:完全没必要用临时文件——extract_msg的附件对象可以直接获取字节数据,不用先存到硬盘上,能简化流程还从根源避免文件占用问题。

修复后的完整代码

import extract_msg
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import os

# 替换为你的实际变量
path = "你的msg文件所在路径"
attachment = "目标msg文件名"
send_to = "收件人邮箱地址"
email_sender = 'automailsender123@gmail.com'
password = 'XXX'

mail = f"{path}/{attachment}"
msg = extract_msg.Message(mail)
msg_sender = str(msg.sender)
msg_subj = str(msg.subject)
msg_message = str(msg.body)
send_to_email = send_to

print("----------------------------------------------------------------------------------------------------")
print(f"Send to: {send_to}\nAttachement: {attachment}\n")

msgMime = MIMEMultipart()
msgMime['From'] = msg_sender
msgMime['To'] = send_to_email
msgMime['Subject'] = msg_subj
body = MIMEText(msg_message)
msgMime.attach(body)

if msg.attachments:
    for att in msg.attachments:
        # 直接获取附件字节数据,跳过临时文件读写
        att_data = att.data
        # 每个附件创建独立的MIMEBase对象,避免内容覆盖
        part = MIMEBase('application', 'octet-stream')
        part.set_payload(att_data)
        encoders.encode_base64(part)
        # 使用附件原始文件名,不要用临时路径
        part.add_header('Content-Disposition', f"attachment; filename= {att.longFilename}")
        msgMime.attach(part)
else:
    print("No attachments")

server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
try:
    server.login(email_sender, password)
except Exception as e:
    print(f"Login failed for user: {email_sender}\nError: {str(e)}")
else:
    text = msgMime.as_string()
    server.sendmail(email_sender, send_to_email, text)
    print("邮件发送成功")
finally:
    server.quit()

关键改动说明

  1. 移除临时文件逻辑:直接通过att.data获取附件的字节内容,绕开文件读写流程,彻底避免文件占用问题。
  2. 循环内新建MIMEBase对象:每次处理一个附件时都创建新的part,确保每个附件的MIME头和内容独立,不会互相覆盖。
  3. 优化错误处理:捕获具体异常并打印错误信息,用else保证只有登录成功才执行发送操作,finally确保服务器连接一定会关闭。

如果因特殊需求必须保留临时文件写法,至少要确保文件句柄被正确关闭:

# 替换原临时文件部分的代码
import tempfile

with tempfile.TemporaryDirectory() as tmp_dir_name:
    for att in msg.attachments:
        att_save_path = os.path.join(tmp_dir_name, att.longFilename)
        att.save(customPath=tmp_dir_name)
        # 使用with语句自动关闭文件,释放句柄
        with open(att_save_path, 'rb') as attachment_stream:
            part = MIMEBase('application', 'octet-stream')  # 循环内新建part
            part.set_payload(attachment_stream.read())
            encoders.encode_base64(part)
            part.add_header('Content-Disposition', f"attachment; filename= {att.longFilename}")
            msgMime.attach(part)

这样修改后,文件会在with块结束后自动关闭,临时目录就能正常清理了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 03:02:33