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

Gmail内嵌图片提取方案失效,求有效方法及X-Attachment-Id提取方案

Gmail邮件导出PDF:内嵌图片提取解决方案

问题描述

我编写了一个脚本,用于将收到的Gmail邮件导出为PDF,所有功能均正常,但无法提取通过Gmail账号发送的内嵌图片。

核心问题:内嵌图片附件(如复制粘贴插入的同名文件grafik.png)与HTML代码中的cid(如ii_l5vcyjv50)无法关联。网上常见的通过message.getRawContent()提取base64再替换cid的方案已失效——要么是getRawContent()结构变化导致正则失效,要么是Google调整了cid关联逻辑。需要找到可靠的方法,将X-Attachment-Ids与对应base64编码提取到数组中。

附原始邮件内容片段:

Dies ist ein Screenshot:
[image: grafik.png]

Dies ist eine per Drag und Drop eingef=C3=BCgte Bilddatei:
[image: amsel.jpg]

Dies ist ein Bild aus Word:
[image: grafik.png]

Dies ist ein Bild direkt aus dem Browser =C3=BCber "Grafik kopieren"
[image: grafik.png]

--=20
Viele Gr=C3=BC=C3=9Fe
Benni

--0000000000008333b505e48ad3d1
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><br><br><div class=3D"gmail_quote"><div dir=3D"ltr" class==3D"gmail_attr">---------- Forwarded message ---------<br>Von: <b class=3D"=gmail_sendername" dir=3D"auto">Summer Moon</b> <span dir=3D"auto"><<a href=3D"mailto:summermoonair@gmail.com">summermoonair@gmail.com</a>></span=><br>Date: Do., 21. Juli 2022 um 20:24=C2=A0Uhr<br>Subject: Gmail Inline Image<br>To: Summer Moon <<a href=3D"mailto:summermoonair@gmail.com">summermoonair@gmail.com</a>><br></div><br><br><div dir=3D"ltr"><div>Dies ist ein Screenshot:</div><div><img src=3D"cid:ii_l5vd00311" alt=3D"grafik.png" width=3D"468" height=3D"263"><br><br></div><div>Dies ist eine per Drag und Drop eingef=C3=BCgte Bilddatei:</div><div><img src=3D"cid:ii_l5vd1fs22" alt=3D"amsel.jpg" width=3D"468" height=3D"222"><br><br></div><div><br></div><div>Dies ist ein Bild aus Word:</div><div><img src=3D"cid:ii_l5vd44ms3" alt=3D"grafik.png" width=3D"468" height=3D"468"><br><br></div><div><br></div><div>Dies ist ein Bild direkt aus dem Browser =C3=BCber "Grafik kopieren"<br></div><div><img src=3D"cid:ii_l5vcyjv50" alt=3D"grafik.png" width=3D"468" height=3D"222"><br><br>-- <br><div dir=3D"ltr" data-smartmail=3D"gmail_signature"><div dir=3D"ltr">Viele Gr=C3=BC=C3=9Fe=C2=A0<div>Benni</div></div></div></div></div>
</div></div>

--0000000000008333b505e48ad3d1--
--0000000000008333b605e48ad3d2
Content-Type: image/png; name="grafik.png"
Content-Disposition: inline; filename="grafik.png"
Content-Transfer-Encoding: base64
Content-ID: <ii_l5vcyjv50>
X-Attachment-Id: ii_l5vcyjv50

iVBORw0KGgoAAAANSUhEUgAAB4AAAAOPCAYAAAAqu9wAAAAABHNCSVQICAgIfAhkiAAAIABJREFU
eF7svdmXJOd53vnmXllbV29oNIAGAZAUaZImJYqmRGksjzSWfSEf6VbnzPHV3M7dXMyl+WdoLnx0
xvacIx/bc6GjsWXNcEYSTJEUKYIUuADEjsbS6LX23DPmeSMruqOiMjIyKzMrt182ClUZ8S3v9/u+
iKzKJ5/3ywVBJwiCnPlXNzB9t9ijq5/z8QP8DAEIQAACEIBAjEAul4bj1AtqWqGpHc+nBza1Pgc1
fDac2fIZFCvnIJBFQL82j/nw37HTH7lgvN+/AxscoH7zT+88PDO4vv5iGFg/yI07vjH7T0zQ2fvP
wPAzT57+e+lJ8WH78b+5Bj3yGcNPqx7FdSqO3NnSfQ6Fcx7Vj+YvlxhQtG6y1ld8bP37elLiXNdS
VqWMTjNOD5qa8NyY3We2P26BrPiy2s/mM3iB5mIXSDyWYePq1/+wdX1suZP7V5Bxn8ricN7z8f77
jaVfuz6+fnEPWz8c98mlHrFKYxZvM61MFOMo/aeNq9/xcY5FnFLbSLtBn1SY9ZizmCbvu6njPOeJ
s68IpxsafHWfs1OqQQACEIAABCAAAQjMjEBxZj3TMQQgAAEIQAACEIAABFaSwHgC7/wjW/bxnZ2B
YcXfszU5MjKBLAVl5AapAAEIQAACEIAABCAAAQhAAAIQWD4CCMDLN6eMCAIQgAAEIAABCEAAAhAQ
gWkIs95mhslsIPtpxDSww1FPRg70M0JrJOxnechG7ZDyy0Qgy2G5TGOd9lhCZ3Dicov4ph2PYvLz
zMW0Z4j2IQABCEAAAhCAAAQgMN8EEIDne36IDgIQgAAEINCXACna+mLhIATmgkDyjfmLDmrW94eZ
j/8CAIwj4k47vFPtn0OrfTy2hPo0q5S6o14/s15/o8Y76fJZ4x9fFDzHotI
gs+KaNIdlaa+fCOxj´´´
SzseH/cyisBZ98+s1Tnv6zAr/qx1ncUnqz7nIQABCEAAAhCAAASWi

解决方案

1. 优先使用Gmail API(最可靠)

别直接解析原始内容,Gmail API已经把附件信息结构化了,步骤如下:

  • 调用users.messages.get接口,指定format='full',获取邮件完整详情,包含所有附件的attachmentId、Content-ID、X-Attachment-Id等字段
  • 遍历邮件的payload.parts,筛选出Content-Disposition为inline且MIME类型以image/开头的附件
  • 对每个符合条件的附件,调用users.messages.attachments.get获取base64编码内容(注意处理URL安全的base64转义)
  • 将HTML中的cid:xxx替换为data:image/[格式];base64,[编码内容]

示例代码(Google Apps Script):

// 提取内嵌图片的cid与base64映射
function getInlineImages(messageId) {
  const gmailMessage = Gmail.Users.Messages.get('me', messageId, {format: 'full'});
  const inlineImages = {};

  // 递归遍历所有邮件部分
  const traverseParts = (parts) => {
    parts.forEach(part => {
      // 检查是否是内嵌图片
      const isInline = part.headers?.some(h => h.name === 'Content-Disposition' && h.value.includes('inline'));
      const isImage = part.mimeType?.startsWith('image/');
      const contentIdHeader = part.headers?.find(h => h.name === 'Content-ID');
      const attachmentId = part.body?.attachmentId;

      if (isInline && isImage && contentIdHeader && attachmentId) {
        const cid = contentIdHeader.value.replace(/[<>]/g, ''); // 去掉尖括号
        // 获取附件base64内容
        const attachment = Gmail.Users.Messages.Attachments.get('me', messageId, attachmentId);
        // 修正URL安全的base64编码
        const base64Data = attachment.data.replace(/-/g, '+').replace(/_/g, '/');
        inlineImages[cid] = `data:${part.mimeType};base64,${base64Data}`;
      }

      // 递归处理子部分
      if (part.parts) traverseParts(part.parts);
    });
  };

  if (gmailMessage.payload.parts) traverseParts(gmailMessage.payload.parts);
  return inlineImages;
}

// 替换HTML中的cid为base64数据URI
function replaceCidWithBase64(html, inlineImages) {
  let updatedHtml = html;
  Object.keys(inlineImages).forEach(cid => {
    updatedHtml = updatedHtml.replace(new RegExp(`cid:${cid}`, 'g'), inlineImages[cid]);
  });
  return updatedHtml;
}

2. 解析原始内容的正则方案(备选)

如果必须用getRawContent(),可以按邮件边界分段处理,避免依赖整体结构:

  • 提取邮件的分隔边界(如--0000000000008333b605e48ad3d2)
  • 将原始内容按边界分割成多个部分
  • 对每个部分,匹配Content-ID、Content-Type和base64编码内容
  • 整理cid与base64的映射关系

示例代码(JavaScript):

function parseRawContentForInlineImages(rawContent) {
  const inlineImages = {};
  // 按邮件边界分割内容
  const parts = rawContent.split(/--[0-9a-f]{20,}/);

  parts.forEach(part => {
    // 匹配Content-ID(不带尖括号)
    const cidMatch = part.match(/Content-ID:\s*<([^>]+)>/i);
    // 匹配图片类型
    const contentTypeMatch = part.match(/Content-Type:\s*(image\/[^\n]+)/i);
    // 确认是base64编码
    const isBase64 = part.match(/Content-Transfer-Encoding:\s*base64/i);

    if (cidMatch && contentTypeMatch && isBase64) {
      const cid = cidMatch[1];
      const contentType = contentTypeMatch[1];
      // 提取空行后的base64内容,去掉换行和无效字符
      const base64Section = part.split(/\n\n/)[1];
      if (base64Section) {
        const base64Data = base64Section.replace(/\n/g, '').replace(/[^A-Za-z0-9+/=]/g, '');
        inlineImages[cid] = `data:${contentType};base64,${base64Data}`;
      }
    }
  });

  return inlineImages;
}

关键注意事项

  • 永远优先用官方API,避免依赖Gmail原始内容的格式(Google随时可能调整)
  • 处理base64时要注意URL安全编码的转换(-→+,_→/)
  • 同名图片只能靠cid区分,必须用cid作为关联键,不能用文件名

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 19:39:19