Gmail APIのインライン画像が表示されない?MIMEパート構造とcid参照を理解して正しく表示する

目次

Gmail APIでメール本文のインライン画像が表示されない?

Gmail APIの users.messages.get でメールを取得し、本文をWebアプリ上に表示する。

一見シンプルに思えるこの処理で、多くの開発者が同じ壁にぶつかる。

本文中に貼り付けられた画像が表示されない。

HTMLソースを確認すると、画像の src 属性に cid:image001.png@01D... のような見慣れない値が入っている。これはメール特有の画像参照方式で、ブラウザ上ではそのままでは解決できない。結果として画像は壊れた状態で表示されるか、[cid:xxx...] という文字列がそのまま本文に現れることになる。

この問題を正しく解決するには、Gmail APIが返すメールデータの構造そのものを理解する必要がある。

Gmail API のリソース MIMEパート構造でインライン画像がどうなっているかを理解する

Gmail APIは、メールのMIME構造を payload.parts としてネストされたツリー構造で返す。

どこに本文や添付画像、インライン画像の情報が入っているかは、メールの構成によって変わる。

「MIME(マイム)タイプ」とは、インターネット上で扱うファイルの種類を識別するための情報です。

主にメールのヘッダーやHTTPレスポンスヘッダーに付与され、ブラウザやメールソフトに対して「そのファイルがどのようなデータ(テキスト、画像、動画など)であるか」を正確に伝える役割を持っています。

スクロールできます
メールの種類mimeType
テキストだけのシンプルなメール
あまりユースケースでないですね、、メールクライアントツールを使用しない場合と考えてよさそう

システム通知やCLIツールからの送信に多い、最もシンプルな構造。payload.body.data に直接本文が入っている。
payload
mimeType: “text/plain”
body.data: “本文(base64url)”
一般的なメール(HTML対応)

Outlook、Gmail、Thunderbirdなど、ほとんどのメールクライアントから送信されるメールはこの構造になる。multipart/alternative の中に、プレーンテキスト版とHTML版が並列で格納されている。受信側のクライアントがどちらか読める方を選んで表示する仕組みだが、今どきのクライアントは基本的にHTML版を使う
payload
mimeType: “multipart/alternative”
parts:
[0] mimeType: “text/plain”, body.data: “プレーンテキスト版”
[1] mimeType: “text/html”, body.data: “HTML版”
添付ファイル付きメール

ファイルを添付すると、multipart/mixed で全体が包まれ、その中に本文パート(multipart/alternative)と添付ファイルパートが並ぶ。本文は1階層深くなる。
payload
mimeType: “multipart/mixed”
parts:
[0] mimeType: “multipart/alternative”
parts:
[0] mimeType: “text/plain”, body.data: “プレーンテキスト版”
[1] mimeType: “text/html”, body.data: “HTML版”

[1] mimeType: “application/pdf”, filename: “資料.pdf”
インライン画像付きメール

本文中に画像を貼り付けたメール、または署名にロゴ画像を含むメールがこの構造になる。multipart/related がインライン画像と本文をグループ化し、HTML本文は cid: で画像を参照する
payload
mimeType: “multipart/related”
parts:
[0] mimeType: “multipart/alternative”
parts:
[0] mimeType: “text/plain”, body.data: “プレーンテキスト版”
[1] mimeType: “text/html”, body.data: “HTML版(<img src=’cid:xxx’>)”
[1] mimeType: “image/png”, filename: “image.png”
インライン画像通常添付ファイル

最も深いネストになるパターン。署名にロゴが入った状態でPDFを添付して送信するような、業務メールでは日常的に発生する構造。本文のHTMLは3階層下に位置する
payload
mimeType: “multipart/mixed”
parts:
[0] mimeType: “multipart/related”
parts:
[0] mimeType: “multipart/alternative”
parts:
[0] mimeType: “text/plain”
[1] mimeType: “text/html” ← 3階層下

[1] mimeType: “image/png”, filename: “logo.png”
[1] mimeType: “application/pdf”, filename: “見積書.pdf”
Gmail API — メール構成パターンとMIMEネスト構造 シンプル payload text/plain HTML対応 multipart/alternative text/plain text/html 添付あり multipart/mixed multipart/alternative text/html text/plain 📎 PDF等 インライン画像あり multipart/related multipart/alternative text/plain text/html (cid:xxx) ↑ img src=”cid:…” で参照 🖼 image/png (Content-Id付き) インライン+通常添付 multipart/mixed multipart/related multipart/alternative text/plain text/html (3階層下) 🖼 image/png (インライン) 📎 application/pdf (通常添付) ← シンプル ———— ネストが深い →

text/html パートの探索は再帰的に確認する必要がある

最上位の parts だけを見る実装では、添付ファイル付きやインライン画像付きのメールで本文が取得できない。mimeTypetext/html のパートが見つかるまで再帰的に parts を掘る実装が必要になる。

(余談)Content-Type: text/plaintext/html とは

メールの本文がどういう形式のデータかを受信側に伝えるための宣言。

HTTPのContent-Typeと同じ仕組みで、MIME(Multipurpose Internet Mail Extensions)という規格で定義されている。

  • text/plain は「装飾なしの生テキスト」。
    受信側のメールクライアントはそのまま等幅フォントなどで表示する。
    太字、色、リンクのクリック、画像の埋め込みなどは一切できない。
  • text/html は「HTMLマークアップされたテキスト」。
    受信側はブラウザと同じようにHTMLをレンダリングする。
    <b>, <a href>, <img>, <table> などが使える。現代のメールクライアント(Gmail, Outlook, Apple Mailなど)はほぼすべてHTML表示に対応している。

プレーンテキスト(text/plain)とは何か

プレーンテキストとは、装飾情報を一切持たない純粋な文字列データのこと。

太字もリンクもフォント指定もない。あるのは文字と、制御文字(改行・タブなど)だけ。

制御文字とは、
画面上に「文字」として直接表示されるのではなく、テキストの配置や区切りをコンピューターに指示するための特殊なデータのことです。
改行(Enter)、タブ(Tab)など

cid参照の仕組み

cid は Content-ID の略で、メールのMIME仕様(RFC 2392)で定義された、メール内の添付リソースを参照するためのURIスキーム。

Webでの https:// と同じように、メールの世界では cid: が添付ファイルへのリンクとして機能する。

インライン画像を含むメールのHTML本文には、以下のような記述がある。

<img src="cid:image.png@01DA2B3C.4E5F6A70">

一方、同じメールの添付パートには Content-Id ヘッダーが設定されている。

Content-Type: image/png
Content-Id: <image.png@01DA2B3C.4E5F6A70>
Content-Disposition: inline

メールクライアント(GmailやOutlookなど)はこの cid:Content-Id を突き合わせて、添付された画像データを本文中に描画する。

ブラウザはこの cid: スキームを解釈できないため、Webアプリ上で表示する場合は開発者が自前で解決する必要がある。

インライン画像をWebアプリ上で表示する方法

基本的なアプローチは、HTML本文中の cid: 参照を data: URI(base64エンコードされた画像データ)に置換することになる。

処理の流れ

  1. 添付ファイル一覧から isInline === true かつ contentTypeimage/* のものを特定する
  2. 各インライン画像の contentIdcontentBytes(base64データ)を取得する
  3. HTML本文中の src="cid:xxx"src="data:image/png;base64,..." に文字列置換する
const resolveInlineImages = (html: string, attachments: Attachment[]) => {
  let resolved = html
  attachments
    .filter(a => a.isInline && a.contentId && a.contentBytes
      && a.contentType?.startsWith('image/'))
    .forEach(a => {
      const cid = a.contentId.replace(/^<|>$/g, '')
      resolved = resolved.replace(
        new RegExp(`src=["']cid:${cid}["']`, 'gi'),
        `src="data:${a.contentType};base64,${a.contentBytes}"`
      )
    })
  return resolved
}

cid: → data: URI 置換の流れ 置換前(ブラウザで表示不可) HTML本文 <img src=”cid:image001@01DA…”> 添付パート Content-Id: <image001@01DA…> cid と Content-Id を突き合わせ 置換後(ブラウザで表示可能) HTML本文 <img src=”data:image/png; base64,iVBORw0KGgo…”> ブラウザ表示結果 画像が壊れて表示 ブラウザ表示結果 画像が正常に表示

Microsoft Graph APIとの違い

同じ「メールの本文と添付ファイルを取得する」という処理でも、Gmail APIとMicrosoft Graph APIではアプローチが大きく異なる。

Gmail APIMicrosoft Graph API
本文の取得MIMEパート構造を自前で解析body.content に展開済みで格納
本文の場所パターンにより異なる(最大3階層下)常に body.content
本文プレビューsnippet(約200文字)bodyPreview(約255文字)
インライン判定パートの headers から自前で判定isInline フラグがそのまま使える
画像データ取得attachmentId で別途API呼び出しcontentBytes が一覧と一緒に返る
cid参照text/html パートの body.data 内(base64urlデコード後)body.content 内にそのまま存在

Gmail APIと違い、

Microsoft Graph APIは、MIME構造の解析をAPI側で処理した上でフラットなレスポンスを返す設計になっている。

Gmail API vs MS Graph API — 本文取得の違い Gmail API MIMEパート構造をそのまま返す multipart/mixed multipart/related multipart/alternative text/html(本文) → 自前で再帰的に掘り出す MS Graph API 解析済みのフラットな構造で返す body.content: “HTML本文” ← そのまま使える bodyPreview: “テキスト要約” hasAttachments: true → MIME解析はAPI側が処理済み

Microsoftのメールのインライン画像のファイル名について

cidとは



メール本文のHTMLには <img src="cid:image001.png"> のような画像参照が含まれています。これはメール内部でしか通用する識別子で、「このメールに添付されている画像ファイルを参照する」という意味です。ブラウザには解決できないURLなので、画像が表示できず破損アイコンになります。

「メール本文中のインライン画像はCIDというID で参照されており、現状そのIDが未解決のため画像が表示できていません。APIから該当の画像データを別途取得し、表示処理に組み込むことで対応可能です。」

画像キー(String)から画像データ(BlobSource)へのマッピングを含む JavaScript オブジェクト。これは、htmlBody パラメータが使用され、これらの画像への参照が <img src="cid:imageKey" /> 形式で含まれていることを前提としています。
https://developers.google.com/apps-script/reference/gmail/gmail-message?hl=ja

画像をメール本文に直接埋め込むには、HTML 属性の content-ID (CID) を使用します。
https://learn.microsoft.com/ja-jp/azure/communication-services/concepts/email/email-attachment-inline

目次