Python使用Gmail API发送邮件时PDF附件无法附加问题
Gmail API 发送邮件PDF附件丢失问题修复
问题现象
- 调用Gmail API实现自动发信功能时,邮件可正常送达,但PDF附件未出现在邮件中;同逻辑下附加
.txt文件可正常显示,仅修改文件后缀为pdf后附件丢失。 - 尝试调用资料中提到的
.add_attach()方法添加附件时,持续报错提示方法不存在,无法验证方案有效性。
复现代码
from email.mime.base import MIMEBase import pickle import os import base64 import googleapiclient.discovery from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.application import MIMEApplication from email.mime.image import MIMEImage from email.message import EmailMessage import mimetypes import imghdr # 读取pickle凭证文件路径 home_dir = os.path.expanduser('~') pickle_path = os.path.join(home_dir, 'gmail.pickle') # 加载凭证 creds = pickle.load(open(pickle_path, 'rb')) # 构建Gmail服务 service = googleapiclient.discovery.build('gmail', 'v1', credentials=creds) # 构造邮件 rec_email = 'sturner@woosox.com' my_email = 'woosoxsuiteinfo@gmail.com' msg = MIMEMultipart('alternative') msg['Subject'] = 'Testing' msg['From'] = my_email msg['To'] = rec_email msgPlain = "Testing Attatchements" msg.attach(MIMEText(msgPlain, 'plain')) # 问题代码段 part = MIMEBase('application', "octet-stream") part.set_payload(open("Something.pdf", "rb").read()) encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="Something.pdf"') msg.attach(part) raw = base64.urlsafe_b64encode(msg.as_bytes()) raw = raw.decode() body = {'raw': raw} message1 = body message = ( service.users().messages().send( userId="me", body=message1).execute()) print('Message Id: %s' % message['id'])
故障原因
- MIME类型初始化错误:创建
MIMEMultipart对象时传入了alternative参数,该子类型用于存储同一内容的不同展示版本(比如纯文本正文、HTML正文),邮件客户端只会选择其中一个可识别的版本渲染,不会将后续添加的二进制文件识别为附件。之前测试txt附件能显示,本质是txt属于text类型,被客户端当成了正文的备选版本渲染,并非作为附件加载。 - 缺失必要模块导入:代码中调用了
encoders.encode_base64()对附件内容编码,但未导入email.encoders模块,该段代码实际运行会直接抛出NameError。 - API混用问题:
.add_attach()是Python 3.6+版本新增的EmailMessage类(新版邮件API)的内置方法,当前代码使用的是MIMEMultipart/MIMEBase这套旧版API,自然不存在该方法,两套API不要交叉使用。
修复步骤
- 修改MIME消息初始化逻辑,将
MIMEMultipart('alternative')改为MIMEMultipart(),默认子类型为mixed,支持同时挂载正文和附件。如果需要同时发送HTML格式正文,需要先创建一个子类型为alternative的MIMEMultipart对象挂载纯文本、HTML两种正文,再把这个对象作为整体挂载到主消息上,最后添加附件。 - 补上缺失的导入语句:
from email import encoders - (可选优化)读取文件时使用
with上下文管理器,避免文件句柄泄漏。
修复后可运行代码
from email.mime.base import MIMEBase from email import encoders import pickle import os import base64 import googleapiclient.discovery from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText # 读取pickle凭证文件路径 home_dir = os.path.expanduser('~') pickle_path = os.path.join(home_dir, 'gmail.pickle') # 加载凭证 with open(pickle_path, 'rb') as f: creds = pickle.load(f) # 构建Gmail服务 service = googleapiclient.discovery.build('gmail', 'v1', credentials=creds) # 构造邮件 rec_email = 'sturner@woosox.com' my_email = 'woosoxsuiteinfo@gmail.com' # 使用默认mixed类型,支持正文+附件结构 msg = MIMEMultipart() msg['Subject'] = 'Testing' msg['From'] = my_email msg['To'] = rec_email msgPlain = "Testing Attachments" msg.attach(MIMEText(msgPlain, 'plain')) # 添加PDF附件 part = MIMEBase('application', "octet-stream") with open("Something.pdf", "rb") as f: part.set_payload(f.read()) encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="Something.pdf"') msg.attach(part) raw = base64.urlsafe_b64encode(msg.as_bytes()) raw = raw.decode() body = {'raw': raw} message = service.users().messages().send(userId="me", body=body).execute() print('Message Id: %s' % message['id'])
内容的提问来源于stack exchange,提问作者Sturner
相关产品推荐
相关产品推荐

