You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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部分的编码设置与内容类型不匹配,以下是修复步骤:

  1. 设置标准的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')
  1. 匹配图片格式与Content-Type
    你将图片保存为PNG格式,但原邮件的图片可能是JPG等其他格式,需同步更新Content-Type头:
# 若保留原图片格式,可动态获取格式
img_format = image.format or 'PNG'
part.set('Content-Type', f'image/{img_format.lower()}')
  1. 避免使用urlsafe_b64encode
    Gmail的原始邮件格式采用标准base64编码,而非url-safe版本(会替换+和/为-和_),不符合邮件MIME标准,因此Gmail无法识别。

  2. 完整修复后的图片处理逻辑

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 16:07:54