Gmail API图片插入异常:调整内嵌图片大小后Gmail无法显示
问题:调整Gmail内嵌图片大小后无法在网页端正常显示
我编写了一个脚本,用于调整Gmail账户中现有邮件里内嵌附件图片的尺寸,但替换原图片后的数据无法在Gmail网页端正常显示。
以下两种尝试中,我都能通过Gmail菜单下载生成的.eml文件,用Python的PIL库Image.open(结合base64.urlsafe_decode)正常读取图片数据,且PIL可将数据保存为系统图片查看器能正常打开的文件,但Gmail网页端始终存在问题:
- 尝试1:使用
set_payload设置原始字节数据后,无法直接用get_payload读取供PIL识别,会触发PIL.UnidentifiedImageError或TypeError; - 尝试2:使用
base64.urlsafe_b64encode编码后设置载荷,可正常读取并保存图片,但Gmail网页端仍不显示图片,仅展示图片前后的文本内容。
代码示例
from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build from google.auth.transport.requests import Request import base64 import email from PIL import Image import io SCOPES = ['https://mail.google.com/'] query = "123456 before:2020-05-30 after:2020-05-20" # 该查询用于定位一封包含两张宽度均超过400像素图片的特定邮件 flow = InstalledAppFlow.from_client_secrets_file(OURSECRETSJSONFILE, SCOPES) creds = flow.run_local_server(port=0) service = build('gmail', 'v1', credentials=creds) res1 = service.users().messages().list(userId='me', q=query).execute() # 也可替换为其他查询语句 fullmsg = service.users().messages().get(userId='me', id=res1['messages'][0]['id'], format='raw').execute() unencoded = base64.urlsafe_b64decode(fullmsg['raw']).decode('utf-8') mmsg = email.message_from_string(unencoded) for part in mmsg.walk(): if part.get_content_maintype() != 'multipart' and part.get('Content-Disposition') is not None and 'image' in part.get_content_type(): # 判断是否为图片类型 image = Image.open(io.BytesIO(part.get_payload(decode=True))) if image.size[0] > 400: image.thumbnail((400,400)) resizedIO = io.BytesIO() image.save(resizedIO, format='png') resizedIO.seek(0) if attempt == 1: part.set_payload(resizedIO.read()) # 用上述Image.open代码读取时会报错: # PIL.UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x04B3D320> # 去掉decode参数则触发: # TypeError: a bytes-like object is required, not 'str' # 在get_payload()后添加.encode()仍会报PIL.UnidentifiedImageError elif attempt == 2: part.set_payload(base64.urlsafe_b64encode(resizedIO.read())) # 用上述Image.open代码读取正常,图片数据可保存、查看,确认无损坏,但Gmail网页端无法显示图片 encmsg = base64.urlsafe_b64encode(mmsg.as_string().encode('utf-8')) temp2 = { 'raw' : encmsg.decode(), 'labelIds' : fullmsg['labelIds'], 'threadId' : fullmsg['threadId']} resp2 = service.users().messages().insert(userId='me', body=temp2, internalDateSource='dateHeader').execute() #service.users().messages().delete(userId='me', id=res1['messages'][0]['id']).execute() # 功能完全正常后再启用删除原邮件的代码
解决方案
问题核心在于邮件MIME部分的编码设置与内容类型不匹配,以下是修复步骤:
- 设置标准的Content-Transfer-Encoding
邮件中传输二进制内容需使用标准base64编码,并在MIME头中声明。直接设置字节数据会被邮件模块当作文本处理,导致编码混乱。修改尝试1的代码:
# 替换原attempt == 1的逻辑 encoded_img = base64.b64encode(resizedIO.read()).decode('utf-8') part.set_payload(encoded_img) part.set('Content-Transfer-Encoding', 'base64')
- 匹配图片格式与Content-Type
你将图片保存为PNG格式,但原邮件的图片可能是JPG等其他格式,需同步更新Content-Type头:
# 若保留原图片格式,可动态获取格式 img_format = image.format or 'PNG' part.set('Content-Type', f'image/{img_format.lower()}')
避免使用urlsafe_b64encode
Gmail的原始邮件格式采用标准base64编码,而非url-safe版本(会替换+和/为-和_),不符合邮件MIME标准,因此Gmail无法识别。完整修复后的图片处理逻辑
for part in mmsg.walk(): if part.get_content_maintype() != 'multipart' and part.get('Content-Disposition') is not None and 'image' in part.get_content_type(): image = Image.open(io.BytesIO(part.get_payload(decode=True))) if image.size[0] > 400: image.thumbnail((400, 400)) resizedIO = io.BytesIO() # 保留原图片格式,若原格式未知则默认用PNG img_format = image.format or 'PNG' image.save(resizedIO, format=img_format) resizedIO.seek(0) # 用标准base64编码并转为字符串 encoded_img = base64.b64encode(resizedIO.read()).decode('utf-8') part.set_payload(encoded_img) # 设置正确的传输编码 part.set('Content-Transfer-Encoding', 'base64') # 更新Content-Type为对应格式 part.set('Content-Type', f'image/{img_format.lower()}')
内容的提问来源于stack exchange,提问作者Blair
相关产品推荐
相关产品推荐

