使用Python调用Gmail API下载受密码保护附件时无法识别文件的问题求助
解决Gmail API无法识别受密码保护附件的问题
我之前也碰到过一模一样的情况!问题根源在于受密码保护的PDF/Excel这类附件,通常会被封装在嵌套的MIME部分里,而不是作为顶级的附件part存在。你当前的代码只遍历了最外层的parts,自然找不到藏在深层的加密文件。
为什么会出现这个问题?
当发送方发送带密码保护的文件时,不少邮件系统会把加密文件打包进multipart/encrypted或者application/pkcs7-mime类型的MIME容器中——这个容器本身不是附件,但真正的加密文件是它的子part。你的代码只检查了顶级part的filename字段,所以直接漏掉了这些嵌套内容。
解决方案:递归遍历所有MIME层级
不管用原生Gmail API还是ezgmail,都需要递归遍历所有层级的MIME parts,才能揪出藏在深层的加密附件。
1. 原生Gmail API的修改版代码
我给你写了个递归函数来遍历所有parts,替换你原来的遍历逻辑就行:
def find_all_attachments(payload): attachments = [] # 如果当前payload包含子part,递归遍历 if 'parts' in payload: for part in payload['parts']: attachments.extend(find_all_attachments(part)) else: # 判断当前part是否为附件:有文件名,或是常见的加密文件类型 filename = payload.get('filename') target_types = [ 'application/pdf', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel', 'application/octet-stream' ] if filename or (payload.get('mimeType') in target_types): attachments.append(payload) return attachments # 替换原代码的遍历逻辑 for message in messages: msg = service.users().messages().get(userId='me', id=message['id']).execute() payload = msg['payload'] all_attachments = find_all_attachments(payload) for att in all_attachments: # 给无文件名的加密文件默认命名 filename = att.get('filename') or f"encrypted_file.{att['mimeType'].split('/')[-1]}" print(f"找到附件: {filename}") # 处理附件下载 if 'data' in att['body']: data = att['body']['data'] else: att_id = att['body']['attachmentId'] att_data = service.users().messages().attachments().get( userId='me', messageId=message['id'], id=att_id ).execute() data = att_data['data'] # 可选:base64解码并保存文件 # import base64 # decoded_data = base64.urlsafe_b64decode(data) # with open(filename, 'wb') as f: # f.write(decoded_data)
2. ezgmail的处理方式
ezgmail的message.attachments只返回顶级附件,所以得拿到原始的Gmail API消息payload来递归处理:
import ezgmail threads = ezgmail.search('LABEL:'+bank) for thread in threads: for message in thread.messages: # 获取ezgmail封装的原始API消息对象 raw_msg = message.msg all_attachments = find_all_attachments(raw_msg['payload']) for att in all_attachments: filename = att.get('filename') or f"encrypted_file.{att['mimeType'].split('/')[-1]}" print(f"找到附件: {filename}") # 后续下载逻辑和上面的原生API代码一致
额外小提示
- 有些加密附件的MIME类型会是
application/octet-stream(二进制流),记得把这个类型加入判断列表。 - 如果遇到
filename为空的情况,可以根据MIME类型自动生成文件名,避免出现无意义的文件名。
内容的提问来源于stack exchange,提问作者Niel
相关产品推荐
相关产品推荐

