Gmail:建立草稿 createDraft()

 


寄信這件事,看起來單純,真的上線自動化時卻容易出包:收件人抄錯、附件忘了、內容沒對齊品牌樣式。

與其直接 send,不如先用 createDraft() 把信生出來、放進草稿匣,給自己或同事最後一眼。這篇會用「能實際複製就能跑」的方式,帶你從 Apps Script 的 GmailApp.createDraft() 開始,把 HTML 版型、inline 圖片、附件、CC/BCC、寄件別名一次講清楚;

也會補上 Gmail API users.drafts.create 的寫法,順手整理權限、配額與大小限制。最後收斂常見錯誤與避雷清單,讓你在正式寄出前,養成穩健的寄信流程。希望本篇文章能夠幫助到需要的您。


目錄

{tocify} $title={目錄} 


為什麼要先建立草稿(Draft)?

        在自動化寄信流程裡,先建立草稿、後審核再送出能降低誤寄與內容錯誤風險;也方便團隊在寄信前加註標籤、微調措辭與附件。對於用 Google Apps Script(下稱 GAS)做通知或行銷郵件的人來說,createDraft() 幾乎是「安全寄信流」的第一站。

        在 GAS 裡,這個能力由 GmailApp.createDraft() 提供;若你走的是 REST 方案,則使用 Gmail API 的 users.drafts.create。二者共同點:都會在使用者信箱建立一封帶有 DRAFT 系統標籤的未寄出郵件;差異在於 GAS 與 API 的開發體驗與權限。


createDraft() 是什麼?能做什麼?

GAS:GmailApp.createDraft(recipient, subject, body, options)

        建立一封草稿郵件;options 可帶 attachments、cc、bcc、from、name、replyTo、htmlBody、inlineImages 等。若同時提供 body 與 htmlBody,支援 HTML 的郵件用戶端會優先顯示 htmlBody。

Gmail API:users.drafts.create

        需先組一封 RFC 2822 的 MIME 訊息、轉成 base64url 後放到 draft.message.raw,再呼叫 API 建立草稿。適合需要跨平台、服務端或 Kotlin/Java 等語言整合的情境。

小提醒:Draft 本身只允許 DRAFT 標籤;寄出時 Draft 會被刪除並產生一封新的 SENT 郵件(ID 也會不同)。


權限、配額與大小限制(一定要先看)

GAS 權限(Scopes)

操作草稿需要 https://mail.google.com/。官方 GmailApp.createDraft() 與 GmailDraft 相關方法皆明確要求此範圍。

Gmail API 權限

可用 https://mail.google.com/、https://www.googleapis.com/auth/gmail.modify 或 https://www.googleapis.com/auth/gmail.compose。 

Apps Script 服務配額

GAS 各服務皆有每日與執行限制,超過會丟出例外並中止執行;大量產草稿前,務必熟悉配額頁。

郵件大小與附件限制

Gmail 單封郵件附件上限為 25 MB(多個附件相加也受限),超過會自動轉成 Drive 連結(並非實體附件)。這個限制也適用你用 createDraft() 建立草稿時。

郵件本體大小(含標頭)有配額

官方在 createDraft() 與 sendEmail() 文件中註記「大小(含標頭,但不含附件)受配額限制」。

寄信上限(非建立草稿)

寄送量的限制由 Gmail 規範(例如 Workspace 常見 2,000 封/日;個人信箱常見 500 封/日),建立草稿不等於寄出,但真正寄送時仍會受限。請以官方頁面為準。


快速開始:用 GmailApp.createDraft() 建立你的第一封草稿

最小可行範例(純文字)

function createDraft_basic() {
  GmailApp.createDraft(
    'user@example.com',
    '主旨:草稿測試',
    '這是一封用 Apps Script 建立的草稿'
  );
}


說明:三個必要參數 recipient、subject、body。收件人支援「逗號分隔」的多筆地址。


HTML 內容 + Inline 圖片 + 附件

function createDraft_html_inline_attachments() { 
  // 1) 從雲端硬碟抓檔案,轉成 Blob 當作附件或內嵌圖
  const file = DriveApp.getFileById('YOUR_FILE_ID');        // e.g. PDF
  const logo = DriveApp.getFileById('YOUR_IMAGE_ID').getBlob(); // 內嵌圖

  // 2) HTML 內容請用 <img src="cid:logo1"> 對應 inlineImages 的 key
  const html = `
    <div style="font-family:system-ui">
      <h2>您好</h2>
      <p>這是一封 HTML 草稿,下面那張圖是 inline:</p>
      <img src="cid:logo1" alt="logo" width="120">
    </div>
  `;

  GmailApp.createDraft(
    'user@example.com',
    '主旨:HTML + Inline 圖 + 附件',
    '(純文字備援內容)',
    {
      htmlBody: html,                         // HTML 優先顯示
      inlineImages: { logo1: logo },          // key 與 cid 對應
      attachments: [file.getAs(MimeType.PDF)] // 官方範例用法
    }
  );

}


重點:

1.    htmlBody 存在時,多數用戶端會優先顯示 HTML;body 仍必填,作為純文字備援。

2.    inlineImages 要用 物件對應 key→Blob,HTML 以 cid:key 內嵌。

3.    附件請提供 BlobSource 陣列,Drive 檔案可 getAs(MimeType.PDF)。


多收件者、CC/BCC、寄件別名

function createDraft_cc_bcc_alias() {
  const aliases = GmailApp.getAliases(); // 取得可用的寄件別名
  const fromAlias = aliases.length ? aliases[0] : null;

  GmailApp.createDraft(
    'a@ex.com,b@ex.com',
    '主旨:多人與別名示範',
    '內容',
    {
      cc: 'c@ex.com,d@ex.com',          // 逗號分隔
      bcc: 'e@ex.com',
      from: fromAlias                    // 必須是 getAliases() 回傳的其中一個
    }
  );
}


cc/bcc 接受 逗號分隔 的電子郵件清單;from 必須是 GmailApp.getAliases() 回傳清單之一,否則無法套用。


進階控管:更新、送出或刪除草稿

建立後你可以取得 GmailDraft 物件並進一步操作:

function after_created_manage() {
  const draft = GmailApp.getDrafts()[0]; // 只是示範,實務上建議存 ID
  draft.update(
    'user@example.com',
    '主旨:更新後的草稿',
    '更新 body',
    { replyTo: 'noreply@example.com' }
  );

  // 寄出
  // const sent = draft.send();

  // 或刪除
  // draft.deleteDraft();
}


GmailDraft 提供 update()(可帶 options)、send() 與 deleteDraft() 等方法,權限仍需 https://mail.google.com/。 


實務常見情境

A. 表單回覆 → 自動產生草稿 → 人工審核後批次寄出

1.    Google 表單寫入試算表。

2.    透過 GAS 讀取新行資料,把收件人與佔位文字套進 HTML 模板。

3.    用 createDraft() 建立草稿;同時在試算表記下 Draft ID。

4.    主管在 Gmail 的「草稿匣」審核(或在試算表把狀態改為「核可」)。

5.    以排程(Trigger)定時讀取「核可」列:GmailApp.getDraft(draftId).send() 寄出。

注意:Draft 只能帶 DRAFT 標籤;想先分類,請在寄出後再以 Thread API/Apps Script 對「寄出的郵件」進行標籤處理。

B. 產出含附件的草稿(自動轉 PDF)

從 Drive 抓原始檔(如 Google 文件/試算表),用 getAs(MimeType.PDF) 轉 PDF 作為附件,再建立草稿。附件總量請控 25 MB 以內;超過會被轉為 Drive 連結。

C. 企業多寄件身分(Alias)

先在 Gmail 設定寄件別名;程式端用 GmailApp.getAliases() 取得清單,options.from 指定其一。若別名不存在,系統會忽略或無法套用。


常見錯誤與雷點

1.    附件或內嵌圖型別不正確

        典型症狀:拋出類型錯誤、圖片不顯示。

        原因與解法:attachments 必須是 BlobSource[];Drive 檔需先 getAs(MimeType.PDF) 等。Inline 圖需在 HTML 以 cid:key 對應 inlineImages 的 key。

2.    HTML 與純文字的關係搞混

        典型症狀:收件端只看到純文字、或 HTML 樣式沒有套用。

        重點:htmlBody 會在支援的用戶端優先顯示,但 body 仍必填 做為備援。

3.    寄件別名(from)無法生效

        典型症狀:顯示還是主帳號;或出現權限/別名錯誤。

        解法:options.from 只能用 getAliases() 回傳的地址。先確認 Gmail 設定中真的有該別名。

4.    CC/BCC 格式錯誤

        典型症狀:收件人欄位被忽略或出現無效地址。

        解法:官方說明為逗號分隔字串;確保沒有多餘空白與分號。

5.    超過附件或郵件大小限制

        典型症狀:建立草稿失敗、或附件被自動換成 Drive 連結。

        解法:附件總量 ≤ 25 MB;更大檔案改用 Drive 連結、壓縮或分檔。

6.    API 端建立 Draft raw 格式錯誤(Gmail API)

        典型症狀:400/422 類錯誤,訊息提到 payload/encoding。

        解法:確保 MIME 內容合規(RFC 2822),並使用 base64url(非一般 base64);放入 draft.message.raw。

7.    誤以為 Draft 可先加自訂標籤

        典型症狀:試圖對 Draft 打標籤沒反應。

        事實:Draft 只能具有 DRAFT 系統標籤;若要分類,請在寄出後對郵件處理。

8.    忽略配額與節流

        典型症狀:GAS 執行中斷、丟出配額錯誤。

        作法:先閱讀 GAS 配額;大量產草稿時加入 sleep/批次,或改由 API/背景任務分流。


品質與可維護性:幾個實用做法

保留 Draft ID:

        建立後把 draft.getId() 寫回資料庫/試算表,後續更新、寄送、刪除都靠它。GmailDraft 亦提供 update() 與 send()。

模板化 HTML:

        用 <span data-field="name"></span> 搭配簡單的字串取代,或用 HtmlService 產生;寄送前總是產草稿,讓人眼檢一次。

附件前先壓縮/轉檔:降低 25 MB 風險與收件端負擔。

寄件別名治理:

        多人共用同一腳本時,事先規範 alias 與 replyTo;避免客服信箱被誤用。


問題集

Q1:建立 Draft 會吃到 Gmail 的寄信上限嗎?

不會直接算寄出量;但真正寄送時仍受 Gmail 寄件限制(例如 Workspace 常見 2,000 封/日)。Apps Script 本身也有服務配額與執行限制,要同步留意。

Q2:如何在 API 端建立含內嵌圖片的 Draft?

用 multipart/related 的 MIME,圖片以 Content-ID 對應 HTML 中的 cid: ;再整封 MIME 做 base64url,放到 draft.message.raw。官方草稿指南說明了建立與更新 Draft 的流程。

Q3:能不能先對 Draft 加上自訂標籤或分類?

不行。Draft 只能帶 DRAFT。建議寄出後再打標籤或移動分類。

Q4:createDraft() 的 options 有哪些?

attachments、cc、bcc、from(必須是 getAliases() 回傳的其中之一)、name、replyTo、htmlBody、inlineImages。


張貼留言 (0)
較新的 較舊