GASでGmailの添付ファイルを
自動でGoogleドライブに保存する方法

請求書や納品書の添付を毎回手でダウンロードして保存する作業は、 GAS(Google Apps Script/Googleサービスを操作できるプログラム)で自動化できます。has:attachmentで添付付きメールを検索し、ドライブへ保存する流れを動くコードで解説します。

|対象: GAS / Gmail / Googleドライブ

Table of Contents

手作業でのファイル保存が抱える問題

取引先から届く請求書PDFや納品書のExcelを、毎回メールから開いて手でドライブに保存していると、 件数が増えるほど手間とミスが増えます。GASで自動化すると、次の問題を一度に解決できます。

保存忘れ・保存漏れ

受信に気づかず添付を取りこぼす事故を防ぎ、確実にドライブへ集約できる

保存先のばらつき

月別フォルダに自動で振り分け、誰が見ても同じ場所に揃う

ダウンロードの手間

1件ずつ開いて保存する単純作業から解放される

他の処理につなげやすい

ドライブに集約すればAI読み取りや台帳転記などに発展させられる

基本:添付を検索してドライブに保存する

まずは基本形です。GmailApp.search()でメールを検索し、getAttachments()で添付を取り出して、folder.createFile()でドライブに書き込みます。処理したスレッドにはラベルを付けて、次回検索から除外します。

function saveAttachmentsBasic() {
  // 保存先フォルダのIDを指定(ドライブのフォルダURL末尾の文字列)
  const FOLDER_ID = "ここに保存先フォルダのIDを貼る";
  const folder = DriveApp.getFolderById(FOLDER_ID);

  // 添付付きの未処理メールを検索(直近7日分に絞る)
  const query = "has:attachment newer_than:7d -label:保存済み";
  const threads = GmailApp.search(query, 0, 20); // 一度に20スレッドまで

  threads.forEach((thread) => {
    thread.getMessages().forEach((message) => {
      // インライン画像(署名画像など)は除外する
      const attachments = message.getAttachments({ includeInlineImages: false });
      attachments.forEach((attachment) => {
        folder.createFile(attachment.copyBlob());
      });
    });
    // 処理済みラベルを付けて二重保存を防ぐ
    thread.addLabel(getOrCreateLabel("保存済み"));
  });
}

// ラベルが無ければ作って返すヘルパー
function getOrCreateLabel(name) {
  return GmailApp.getUserLabelByName(name) || GmailApp.createLabel(name);
}

検索クエリはGmailの検索窓と同じ演算子が使えます。has:attachmentが「添付あり」、newer_than:7dが「7日以内」、-label:保存済みが「保存済みラベルを除く」という意味です。フォルダIDはドライブでフォルダを開いたときのURL末尾の文字列を使います。

月別フォルダを自動で作って振り分ける

1つのフォルダに全ファイルを入れ続けると、すぐに見づらくなります。 「2026-07」のような月別サブフォルダに自動で振り分けると、後から探しやすくなります。 同名フォルダを重複して作らないよう、getFoldersByName()で存在チェックしてからcreateFolder()するのがポイントです。

// 「2026-07」のような月別サブフォルダを取得(無ければ作成)
function getMonthlyFolder(parentId) {
  const parent = DriveApp.getFolderById(parentId);
  const name = Utilities.formatDate(
    new Date(),
    "Asia/Tokyo",
    "yyyy-MM"
  );

  // 同名フォルダの重複作成を防ぐため、まず既存を探す
  const it = parent.getFoldersByName(name);
  if (it.hasNext()) {
    return it.next();
  }
  return parent.createFolder(name);
}

function saveIntoMonthlyFolder() {
  const PARENT_ID = "ここに親フォルダのIDを貼る";
  const folder = getMonthlyFolder(PARENT_ID);

  const threads = GmailApp.search("has:attachment -label:保存済み", 0, 20);
  threads.forEach((thread) => {
    thread.getMessages().forEach((message) => {
      message.getAttachments({ includeInlineImages: false }).forEach((att) => {
        folder.createFile(att.copyBlob());
      });
    });
    thread.addLabel(getOrCreateLabel("保存済み"));
  });
}

フォルダ名はUtilities.formatDate()で作ります。タイムゾーンに"Asia/Tokyo"を指定しないと、月をまたぐ深夜の実行で日付がずれることがあるので必ず指定します。

PDFだけ・特定の送信者だけに絞り込む

「請求書PDFだけをドライブに集めたい」といった場合は、送信者と添付の種類の両方で絞り込みます。 送信者は検索クエリのfrom:で、添付の種類はgetContentType()(MIMEタイプ=ファイル形式を表す文字列)とファイル名の拡張子で判定します。

// PDFだけを保存する例(拡張子とMIMEタイプの両方で判定)
function savePdfOnly() {
  const FOLDER_ID = "ここに保存先フォルダのIDを貼る";
  const folder = DriveApp.getFolderById(FOLDER_ID);

  // 送信者を絞ると誤保存を防げる
  const query = "has:attachment from:invoice@example.com -label:保存済み";
  const threads = GmailApp.search(query, 0, 20);

  threads.forEach((thread) => {
    thread.getMessages().forEach((message) => {
      message.getAttachments({ includeInlineImages: false }).forEach((att) => {
        const name = att.getName().toLowerCase();
        const isPdf =
          att.getContentType() === "application/pdf" || name.endsWith(".pdf");
        if (!isPdf) return;              // PDF以外はスキップ
        if (att.getSize() < 1024) return; // 1KB未満の小さすぎる添付も除外

        folder.createFile(att.copyBlob());
      });
    });
    thread.addLabel(getOrCreateLabel("保存済み"));
  });
}

getSize()で極端に小さいファイルを弾いておくと、署名や透明画像のような不要ファイルの保存を防げます。 Excelを対象にしたいときは.xlsxや対応するMIMEタイプで条件を差し替えてください。

ファイル名を付け替えて保存する

添付ファイル名が「invoice.pdf」のように同じだと、ドライブ上で見分けがつきません。 受信日や送信者を頭に付けてリネームしておくと、後から探しやすくなります。copyBlob()でコピーを作り、setName()で名前を付け替えてから保存します。

// 「日付_送信者_元のファイル名」でリネームして保存する
function saveWithRenamedFiles() {
  const FOLDER_ID = "ここに保存先フォルダのIDを貼る";
  const folder = DriveApp.getFolderById(FOLDER_ID);

  const threads = GmailApp.search("has:attachment -label:保存済み", 0, 20);
  threads.forEach((thread) => {
    thread.getMessages().forEach((message) => {
      const dateStr = Utilities.formatDate(
        message.getDate(),
        "Asia/Tokyo",
        "yyyyMMdd"
      );
      // 送信者のメールアドレス部分だけ取り出す
      const from = (message.getFrom().match(/[\w.+-]+@[\w.-]+/) || ["unknown"])[0];

      message.getAttachments({ includeInlineImages: false }).forEach((att) => {
        const blob = att.copyBlob();
        blob.setName(dateStr + "_" + from + "_" + att.getName());
        folder.createFile(blob);
      });
    });
    thread.addLabel(getOrCreateLabel("保存済み"));
  });
}

送信者アドレスはgetFrom()が「山田太郎 <taro@example.com>」の形で返すため、正規表現でメールアドレス部分だけを取り出しています。 ドライブは同名ファイルの重複を許すので、二重保存の防止はラベルで行う点に注意してください。

トリガーで定期実行する

手で実行するのではなく、時間主導トリガー(決めた間隔で自動実行する仕組み)で回せば、 受信した添付が自動的にドライブへ溜まっていきます。 トリガーは画面からも設定できますが、ScriptApp.newTrigger()でコードからも作れます。

// 1時間ごとに実行するトリガーをコードから設定する
function createHourlyTrigger() {
  // 重複作成を防ぐため、既存の同名トリガーを削除してから作る
  ScriptApp.getProjectTriggers().forEach((t) => {
    if (t.getHandlerFunction() === "saveIntoMonthlyFolder") {
      ScriptApp.deleteTrigger(t);
    }
  });

  ScriptApp.newTrigger("saveIntoMonthlyFolder")
    .timeBased()
    .everyHours(1)
    .create();
}

このトリガー作成関数を一度だけ実行すれば設定完了です。 既存の同名トリガーを先に削除しているので、何度実行してもトリガーが増殖しません。 間隔はeveryHours(1)の数字を変えて調整できます。

実務での注意点

1. 1回の処理件数を絞る

GASには1回の実行が6分までという制限があります。search()の第3引数で取得スレッド数を20件程度に抑え、少しずつ処理するのが安全です。ラベルで処理済みを除外していれば、次回の実行で続きを自動的に処理できます。

2. インライン画像を除外する

getAttachments()は署名やロゴなどのインライン画像も拾うことがあります。includeInlineImagesをfalseにするか、サイズ・拡張子でフィルタして、不要ファイルの保存を防ぎましょう。

3. 二重保存はラベルで防ぐ

ドライブは同名ファイルを重複して作成できてしまいます。処理済みスレッドにラベルを付け、検索クエリで除外する方式にすれば、同じ添付を何度も保存する事故を確実に防げます。

4. 保存容量と権限に注意

大量の添付を自動保存すると、Googleドライブの容量を消費します。不要な種類は絞り込み、保存先フォルダの共有範囲(誰が見られるか)も事前に確認しておきましょう。初回実行時はGmailとドライブへのアクセス許可を求められます。

まとめ

Gmailの添付ファイル保存は、has:attachmentでの検索・getAttachments()での取り出し・folder.createFile()での保存という3ステップが基本です。 月別フォルダの自動振り分け、PDFや送信者での絞り込み、ファイル名のリネームを組み合わせれば、実務で使える整理の仕組みになります。 処理済みラベルで二重保存を防ぎ、時間主導トリガーで定期実行すれば、添付ファイルの保存作業を完全に自動化できます。

よくある質問

GmailApp.search()の検索クエリに has:attachment を含めます。Gmailの検索窓と同じ演算子が使えるので、has:attachment from:example.com newer_than:7d のように送信者や期間を組み合わせて絞り込むのが実務では確実です。無関係なメールまで処理しないよう、条件はできるだけ具体的にするのがおすすめです。

処理済みのスレッドにラベル(例:「保存済み」)を付け、検索クエリで -label:保存済み と除外するのが定番です。ラベルはthread.addLabel()で付けられます。ラベル方式なら、スクリプトのプロパティに保存済みIDを溜め込む必要がなく、Gmail上でも処理状況が一目で分かります。

getAttachments()には署名画像やインライン画像が含まれることがあります。message.getAttachments({includeInlineImages: false}) を指定するとインライン画像を除外できます。さらにattachment.getSize()でサイズの小さいものを弾いたり、拡張子で絞り込むと、不要なファイルの保存を防げます。

できます。attachment.getContentType()(MIMEタイプ)やattachment.getName()の拡張子で判定して絞り込みます。例えばPDFは application/pdf、名前が .pdf で終わるもの、という条件でフィルタします。請求書PDFだけをドライブに集約したい、といった用途に向いています。

いいえ。DriveApp.getFolderById()で親フォルダを取得し、getFoldersByName()で目的の名前のサブフォルダを探し、無ければcreateFolder()で作る、という関数を用意すれば「2026-07」のような月別フォルダを自動生成できます。同名フォルダの重複作成を避けるため、必ず存在チェックをしてから作成してください。

GAS開発・業務Webシステムを
相談する。

添付ファイルの自動保存以外にも、Gmail振り分け・帳票出力・ドライブのファイル整理など、日々の業務を自動化するGAS開発をご相談いただけます。

AI×GAS自動化を見る