Gmail:寄送信件 sendEmail()

 


如果你剛好負責內部通知、活動邀請或會員電子報,sendEmail() 會是最稱職的小幫手。

本文用白話的方式把重點拆開:基礎語法、可用參數、HTML 與純文字並存、附檔與內嵌圖、cc/bcc、replyTo 與別名發信,還會示範如何把名單放在 Google 試算表裡跑批次。

中段整理 Gmail 與 Apps Script 的配額限制,末段直接列出常見錯誤與避雷清單,照著做就不怕被「寄太多」或「寄不出去」絆住。希望本篇文章能幫助到需要的您。


目錄

{tocify} $title={目錄} 


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

在 GAS 中你可以用兩種服務寄信:

GmailApp.sendEmail(...):

        能存取郵件、草稿、標籤、別名等 Gmail 能力;寄信時支援 HTML、附件、內嵌圖片、別名發信、cc/bcc、replyTo、noReply 等參數。主旨長度上限 250 字元。

MailApp.sendEmail(...):

        只專注「寄信」,不讀信箱;同樣支援 HTML、附件、cc/bcc、replyTo,也有 getRemainingDailyQuota() 可查寄件額度。

小提醒:兩者都受 Apps Script 配額 與 Gmail 郵件大小/收件者數 限制。


最簡單上手:純文字與 HTML 郵件

純文字三行搞定

function sendPlain() {
  GmailApp.sendEmail('to@example.com', '測試主旨', '這是一封純文字測試信。');
}


recipient 可用逗號分隔多個地址;主旨上限 250 字元。


HTML 內容(含純文字備援)

function sendHtml() {
  const html = '<h2>您好</h2><p>這是 <b>HTML</b> 測試。</p>';
  GmailApp.sendEmail(
    'to@example.com',
    'HTML 測試',
    '如果看不到 HTML,會顯示這段純文字',
    { htmlBody: html }
  );
}


支援 htmlBody,收件端可顯示 HTML;同時保留第三個參數作為純文字備援。


常用進階參數:附件、內嵌圖、cc/bcc、別名、回覆位址

夾帶附件(例如把雲端硬碟檔案轉成 PDF)

function sendWithAttachment() {
  const file = DriveApp.getFileById('你的檔案ID');
  GmailApp.sendEmail(
    'to@example.com',
    '附檔測試',
    '詳見附件',
    { attachments: [file.getAs(MimeType.PDF)], name: '自動化寄信' }
  );
}


attachments 接受 BlobSource[];name 可指定寄件者顯示名稱。


內嵌圖片(用 CID)

function sendInlineImage() {
  const logo = UrlFetchApp.fetch('要放的圖片路徑').getBlob().setName('logo');
  const html = '<p>這是內嵌圖:</p><img src="要放的圖片路徑" />';
  GmailApp.sendEmail(
    'to@example.com',
    '內嵌圖片測試',
    '純文字備援',
    { htmlBody: html, inlineImages: { brandLogo: logo } }
  );
}


inlineImages 是「鍵名 → Blob」的映射;在 htmlBody 用 <img src="cid:鍵名"> 引用。


多收件人、cc/bcc

function sendWithCcBcc() {
  GmailApp.sendEmail(
    'a@example.com,b@example.com',
    '群發測試',
    '正文',
    { cc: 'manager@example.com', bcc: 'audit@example.com' }
  );
}


to/cc/bcc 皆以逗號分隔清單。每封信的收件者上限 50 位(含 to/cc/bcc 總和)。


指定「回覆至」與「No-Reply」(僅 Workspace 可用)

function sendWithReplyToAndNoReply() {
  GmailApp.sendEmail(
    'to@example.com',
    '回覆位址測試',
    '正文',
    { replyTo: 'support@yourdomain.com', noReply: true } // noReply 僅 Workspace
  );
}


noReply: true 僅 Google Workspace 帳號可用;個人 Gmail 不支援。


用「別名」發信(From)

function sendFromAlias() {
  const aliases = GmailApp.getAliases(); // 先取出帳號的可用別名
  if (aliases.length === 0) throw new Error('沒有已設定的別名');
  GmailApp.sendEmail(
    'to@example.com',
    '別名發信測試',
    '正文',
    { from: aliases[0] }
  );
}


要能用 from 指定別名,必須先在 Gmail 的「以…寄出郵件(Send mail as)」裡新增並驗證該地址。新增後 getAliases() 才會列出,from 參數才能使用。設定路徑:Gmail → 設定 → 帳戶與匯入 → 以…寄出郵件。


草稿工作流:先建立 Draft、確認後再寄

想先在 Gmail 裡預覽再送出,可以先建草稿,再由程式或人工送出。

function draftThenSend() {
  const draft = GmailApp.createDraft(
    'to@example.com',
    '先當草稿',
    '正文',
    { htmlBody: '<p>先預覽</p>' }
  );
  // 程式直接寄出草稿:
  const message = draft.send(); // 回傳 GmailMessage
  Logger.log('已送出時間:' + message.getDate());
}


createDraft() 會回傳 GmailDraft,可再 send() 送出,也可 update() 修改內容。


實務情境範例

1. 用試算表名單群發(含 HTML、附件、追蹤錯誤)

function bulkSendFromSheet() {
  const sh = SpreadsheetApp.getActive().getSheetByName('mailing');
  const rows = sh.getDataRange().getValues(); // 第一列標題:email, name, pdfFileId
  const header = rows.shift();
  const COL = Object.fromEntries(header.map((h,i)=>[h,i]));

  rows.forEach((r, idx) => {
    try {
      const to = String(r[COL.email]||'').trim();
      if (!to) throw new Error('缺少 email');
      const name = r[COL.name] || '';
      const fileId = r[COL.pdfFileId];
      const attachments = fileId ? [DriveApp.getFileById(fileId).getAs(MimeType.PDF)] : [];
      const html = `<p>${name} 您好:</p><p>這是通知內容。</p>`;

      GmailApp.sendEmail(
        to,
        `【通知】Hello, ${name}`,
        '純文字備援內容',
        { htmlBody: html, attachments }
      );

      sh.getRange(idx+2, header.length+1).setValue('OK'); // 寫入狀態
    } catch (e) {
      sh.getRange(idx+2, header.length+1).setValue('ERR: ' + e.message);
      Utilities.sleep(500); // 避免過於頻繁呼叫
    }
  });
}


2.    表單回覆自動寄信(時間/表單觸發)

在編輯器:觸發條件 → 新增觸發器,綁定 onFormSubmit 或時間驅動觸發,每分鐘/每小時批次寄送。

觸發器有每日執行總時長限制,避免一次寄太多。


必讀限制與配額(避免一寄就被擋)

1.    每日收件者數(per day):個人 Gmail 約 100 位/天;Workspace 約 1,500 位/天(同網域可到 2,000)。配額以「收件者數」計(to+cc+bcc)。

2.    每封收件者上限(per message):50 位。

3.    附件總大小:25 MB/封;附件數量 250 個/封;本文大小上限(非附件)視帳號等級而定(常見 200–400KB)。

4.    主旨長度:上限 250 字元。

5.    配額重置:以首次請求後 24 小時為窗口滾動重置(非整點零時)。詳細以官方頁面為準。

想知道今天還能寄多少?用 MailApp.getRemainingDailyQuota() 查詢剩餘收件者數。


常見錯誤與雷點

1.    Exception: Service invoked too many times / 超出配額

        原因:短時間或當天寄太多。

        對策:批次處理加 Utilities.sleep() 節流;拆批次;使用時間觸發器分散;在寄送前檢查 getRemainingDailyQuota()。

2.    收件者過多或格式錯誤

        症狀:50 人以上被擋、逗號/空白錯誤。

        對策:每封 ≤ 50 位(含 to/cc/bcc),用逗號分隔,寄多批。

3.    附件過大或過多

        症狀:25MB 以上失敗、或 250 個附件超限。

        對策:壓縮、轉 PDF、改分享連結,或分封寄送。

4.    HTML 信換行跑版

        原因:把 \n 當成 HTML 換行。

        對策:在 htmlBody 使用 <br> 或 <p>;保留第三參數純文字備援。(HTML 行為屬常識,但請留意 htmlBody/純文字差異)

5.    內嵌圖片顯示不出來

        原因:inlineImages 的鍵名與 <img src="cid:鍵名"> 不一致,或 Blob 沒名稱。

        對策:確認鍵名一致、Blob 設好名稱,並在 htmlBody 以 cid: 方式引用。

6.    指定 from 但被忽略或報錯

        原因:別名未在 Gmail「以…寄出郵件」中新增/驗證成功;或該地址不在 getAliases() 列表。

        對策:先到 Gmail 設定新增並驗證,再用 GmailApp.getAliases() 檢查;options.from 只能用該清單中的地址。

7.    noReply: true 無效

        原因:個人 Gmail 帳號不支援。

        對策:需要 Workspace 帳號才可用 noReply。

8.    一次寄太快導致暫時性錯誤

        對策:加退避(exponential backoff),例如每 10 封 sleep 1–2 秒;遇錯誤重試限定次數。

9.    想先讓同事確認內容再寄

        對策:先 createDraft(),讓同事在 Gmail 裡看草稿再 send() 寄出。


GmailApp vs MailApp:該用哪一個?

1.    要用 別名發信/存取草稿/讀取郵件、標籤 → GmailApp。

2.    只想「單純寄信 + 查剩餘額度」→ MailApp 較精簡,並提供 getRemainingDailyQuota()。

3.    兩者同樣受每日收件者數與每封收件者上限/大小等限制。


權限與發信身分

1.    GmailApp 需要 https://mail.google.com/ 範圍授權(首次執行會跳出授權畫面)。

2.    使用別名(from)前,請先在 Gmail 「以…寄出郵件(Send mail as)」 新增與驗證;否則無法用該地址發信。


問題集

Q1:可以同時寄給很多人嗎?

    可以,但每封最多 50 位(含 to/cc/bcc),且每天有收件者總量配額(個人 100、Workspace 1,500 起)。群發請分批。

Q2:如何知道今天還能寄多少?

    用 MailApp.getRemainingDailyQuota() 取得剩餘收件者數。

Q3:為什麼我指定 from 沒生效?

    因為那個地址未在 Gmail 設定中新增為別名或尚未驗證成功;需先設定,並確認 GmailApp.getAliases() 有列出它。

Q4:想先預覽信件再寄?

    用 GmailApp.createDraft() 建草稿;確認 OK 後可在程式裡 draft.send() 寄出。


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