使用Python批量发送邮件时附件文件名异常问题排查
批量发送邮件附件扩展名丢失问题排查与解决
我用Python脚本(作为Flask应用函数运行)给客户批量发邮件,遇到一个问题:部分客户反馈收到的附件是无扩展名的异常文件(比如邮箱为test@gmail.com时,附件名为test),但我自己测试收到的附件是正常的。附件文件名应该是「邮箱 - FS 带日期的文档编号.pdf」格式。我怀疑是MIMEBase相关代码导致的问题,相关代码如下:
import email, smtplib, ssl import os from email import encoders from email.mime.base import MIMEBase from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText def send_mail(): subject = "YesNet Faktura" body = "Dzień dobry,\n\nprzesyłamy e-fakturę, którą znajdą Państwo w załączniku.\nDziękujemy za terminowe dokonanie płatności.\n\nPozdrawiamy,\nyesNET.pl\n\nTa wiadomość została wygenerowana automatycznie. Prosimy na nią nie odpowiadać. Zapytania można kierować na: EMAIL" sender_email = "EMAIL" receiver_email = "" password = "PASSWORD" # assign directory a = "uploads\ " a = a.strip() directory = 'uploads' # iterate over files in # that directory emailsLog = [] if len(os.listdir(directory)) > 0: for filename in os.listdir(directory): f = os.path.join(directory, filename) # checking if it is a file print(f) print(filename) if os.path.isfile(f): filename = "" receiver_email = "" text = "" context = "" part = "" message = 0 recievers = "" filename = f receiver_email = f.replace(a, "") recievers = receiver_email.split() # Create a multipart message and set headers # recipients = [receiver_email, "EMAIL"] recipients = ["EMAIL"] while recievers[1] != "FS": recipients.append(recievers[0]) recievers.pop(0) message = MIMEMultipart() message["From"] = sender_email message["Subject"] = subject message["To"] = ", ".join(recipients) message["Bcc"] = ", ".join(recipients) # Recommended for mass emails # Add body to email message.attach(MIMEText(body, "plain")) # Open PDF file in binary mode with open(filename, "rb") as attachment: # Add file as application/octet-stream # Email client can usually download this automatically as attachment part = MIMEBase("application", "octet-stream") part.set_payload(attachment.read()) # Encode file in ASCII characters to send by email encoders.encode_base64(part) # Add header as key/value pair to attachment part filename = filename.replace(a, "") part.add_header( "Content-Disposition", f"attachment; filename= {filename}", ) message.attach(part) text = message.as_string() # Log in to server using secure context and send email context = ssl.create_default_context() with smtplib.SMTP_SSL("SMTP", 465, context=context) as server: server.login(sender_email, password) server.sendmail(sender_email, recipients, text) print(receiver_email) emailsLog.append(receiver_email) return emailsLog else: return False
我怀疑这段代码存在问题:
with open(filename, "rb") as attachment: # Add file as application/octet-stream # Email client can usually download this automatically as attachment part = MIMEBase("application", "octet-stream") part.set_payload(attachment.read())
排查方向与解决建议
1. 修复文件名头部的空格问题
代码中Content-Disposition头部的filename= {filename}里,等号后有个多余空格,部分邮件客户端(如Gmail)解析时会截断文件名或误判格式,导致扩展名丢失。去掉空格并给文件名加双引号,避免特殊字符解析异常:
part.add_header( "Content-Disposition", f"attachment; filename=\"{filename}\"", )
2. 对文件名进行RFC2231编码
非ASCII字符在不同邮件客户端的解析规则不一致,用email.utils.encode_rfc2231编码文件名,确保跨客户端兼容性:
from email.utils import encode_rfc2231 # 替换原有的add_header部分 filename_encoded = encode_rfc2231(filename, 'utf-8') part.add_header( "Content-Disposition", f"attachment; filename*=UTF-8''{filename_encoded}" )
3. 使用正确的MIME类型处理PDF
PDF文件应指定application/pdf作为MIME类型,而非通用的octet-stream,这样邮件客户端能更准确识别文件类型,即使文件名异常也可能自动补全扩展名:
with open(filename, "rb") as attachment: part = MIMEBase("application", "pdf") part.set_payload(attachment.read())
更推荐使用MIMEApplication(专门处理二进制应用文件):
from email.mime.application import MIMEApplication with open(filename, "rb") as attachment: part = MIMEApplication(attachment.read(), _subtype='pdf')
4. 修正文件名提取逻辑
原代码用字符串替换和split处理文件名的方式容易出错,比如文件名含空格时会破坏结构。改用os.path.basename直接获取上传目录下的文件名:
# 替换原有的filename赋值逻辑 filename = os.path.basename(f) # 从文件名提取收件人邮箱(假设文件名格式为「邮箱 - FS ...pdf」) receiver_email = filename.split(" - ")[0]
5. 多客户端测试验证
用Gmail、Outlook、本地邮件客户端等不同平台测试,确认问题是否仅出现在特定客户端,帮助定位是编码问题还是解析差异。
内容的提问来源于stack exchange,提问作者ItsJust PaperMan
相关产品推荐
相关产品推荐

