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

使用Gmail API+Rails无法显示邮件HTML中的内嵌图片求助

解决Gmail API内嵌图片(CID引用)显示破碎的问题

哈哈,这个坑我踩过好几次!Gmail里这种用cid:引用的内嵌图片,根本不是直接嵌在HTML文本里的,而是作为邮件的内嵌附件存在的。你需要把CID和对应的图片数据关联起来,替换掉HTML里的src属性,才能让浏览器正常显示图片。下面给你一步步拆解解决方案:

1. 确保获取完整的邮件内容

默认调用Gmail API的users.messages.get接口时,可能只返回了邮件的基本结构,没有包含附件数据。你需要在请求中指定format=FULL,这样才能拿到所有内嵌附件的元信息和ID:

GET https://www.googleapis.com/gmail/v1/users/{userId}/messages/{messageId}?format=FULL

2. 建立CID到图片数据的映射

邮件的payload里会包含所有的邮件部件(parts),其中内嵌附件会满足两个条件:

  • 带有body.attachmentId(表示是附件)
  • 头部(headers)里包含Content-ID字段,值就是HTML里引用的CID(注意原始的Content-ID通常带尖括号,比如<ii_jfi5vwc30_1628627122d12121>,需要去掉前后的尖括号才能和HTML里的cid:ii_jfi5vwc30_1628627122d12121匹配)

你需要递归遍历所有parts,收集每个CID对应的附件数据,然后转换成Data URI格式(浏览器可以直接识别的base64编码图片)。

3. 获取附件数据并转换为Data URI

对于每个找到的内嵌附件,调用users.messages.attachments.get接口获取base64编码的图片数据,然后解码并重新编码成标准的base64格式,拼接成Data URI:

# 示例:Python中获取并转换附件数据
attachment = service.users().messages().attachments().get(
    userId='me', messageId=message_id, id=attachment_id
).execute()
# 解码Gmail的url-safe base64数据
raw_data = base64.urlsafe_b64decode(attachment['data'])
# 转换成标准base64并生成Data URI
mime_type = part['mimeType']  # 比如image/png、image/jpeg
data_uri = f"data:{mime_type};base64,{base64.b64encode(raw_data).decode('utf-8')}"

4. 替换HTML中的CID引用

拿到CID和Data URI的映射后,遍历HTML内容,把所有src="cid:xxx"替换成对应的Data URI即可。比如把<img src="cid:ii_jfi5vwc30_1628627122d12121">替换成<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...">。

完整代码示例(Python)

import base64
from googleapiclient.discovery import build

# 假设已经完成Gmail API的授权,得到service对象
message_id = "你的目标邮件ID"

# 1. 获取完整邮件内容
full_message = service.users().messages().get(
    userId='me', id=message_id, format='FULL'
).execute()

# 2. 遍历所有parts,建立CID到Data URI的映射
cid_to_data_uri = {}

def traverse_parts(parts):
    for part in parts:
        if 'parts' in part:
            traverse_parts(part['parts'])
        
        # 查找Content-ID头部
        content_id = None
        for header in part.get('headers', []):
            if header['name'].lower() == 'content-id':
                content_id = header['value'].strip('<>')  # 去除尖括号
                break
        
        # 如果是带CID的内嵌附件,获取并转换数据
        if content_id and part.get('body', {}).get('attachmentId'):
            attachment = service.users().messages().attachments().get(
                userId='me', messageId=message_id, id=part['body']['attachmentId']
            ).execute()
            raw_data = base64.urlsafe_b64decode(attachment['data'])
            mime_type = part['mimeType']
            data_uri = f"data:{mime_type};base64,{base64.b64encode(raw_data).decode('utf-8')}"
            cid_to_data_uri[content_id] = data_uri

# 开始遍历payload中的parts
traverse_parts(full_message['payload'].get('parts', []))

# 3. 提取HTML内容并替换CID引用
html_content = None
# 查找text/html类型的part
for part in full_message['payload'].get('parts', []):
    if part['mimeType'] == 'text/html':
        html_content = base64.urlsafe_b64decode(part['body']['data']).decode('utf-8')
        break

# 替换所有CID引用
if html_content and cid_to_data_uri:
    for cid, uri in cid_to_data_uri.items():
        html_content = html_content.replace(f'cid:{cid}', uri)

# 现在html_content中的内嵌图片就能正常显示了

注意事项

  • 邮件的parts可能是嵌套结构(比如multipart/related包含text/html和内嵌附件),所以一定要递归遍历所有parts,不要只处理顶层的parts。
  • Content-ID是大小写敏感的,匹配时要严格对应HTML里的CID值。
  • 确保你的API授权有gmail.readonly或更高权限,否则无法获取附件数据。

内容的提问来源于stack exchange,提问作者MeeSN

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:17:40