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()
关键改动说明
- 移除临时文件逻辑:直接通过
att.data获取附件的字节内容,绕开文件读写流程,彻底避免文件占用问题。 - 循环内新建MIMEBase对象:每次处理一个附件时都创建新的
part,确保每个附件的MIME头和内容独立,不会互相覆盖。 - 优化错误处理:捕获具体异常并打印错误信息,用
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
相关产品推荐
相关产品推荐

