こんな人にオススメの記事です
- メールの添付ファイルを自動的に整理し、保存したい人
- 毎日大量の添付ファイルを受信する人
- Gmailをもっと便利に使いたい人
Gmailを日常的に使っていると、多くの添付ファイルを受信することが多いですよね。
特に個人事業主やフリーランサーの方にとって、これらのファイルを整理し、確実に保存しておくことは重要です。
しかし、毎回手動で保存するのは面倒ですし、誤って削除してしまうリスクもあります。
また、受信メールが増えることで、Googleアカウントの容量がいっぱいになってしまう可能性もあります。
この記事では、Google Apps Script(GAS)を使って、Gmailの添付ファイルを自動でGoogleドライブに保存する方法を紹介します。
ソースコードも載せてあるので、この記事の手順に沿って進めることで、誰でも簡単に設定できます。
Gmailの添付ファイルを自動でGoogleドライブに保存する方法
準備するもの
まずは、この自動化を実現するために必要なツールとアカウントを確認しましょう。
- Gmailアカウント: 添付ファイルを受信するGmailアカウント
- Googleドライブアカウント: ファイルを保存するGoogleドライブ
- Google Apps Script(GAS): Googleの提供するスクリプト環境
Googleドライブで保存先のフォルダを作成し、フォルダIDを取得する
まずは以下の手順でGoogleドライブ内に添付ファイルを保存するためのフォルダを作成し、フォルダIDを取得してください。
- Googleドライブを開き、添付ファイルを保存したいフォルダを作成
- 作成したフォルダを開く
- ブラウザのアドレスバーに表示されているURLを確認し、フォルダのIDを取得する
Googleドライブを開き、空白箇所を右クリックし、[新しいフォルダ]をクリックしてください。

[新しいフォルダ]をクリックすると「新しいフォルダ」と書かれた画面がポップアップします。
その中にわかりやすい名前を入力し、[作成]をクリックしてください。

[作成]をクリックすると、ひとつ前の画面に戻り、作成したフォルダが表示されています。
そのフォルダを開いてください。

ブラウザのアドレスバーに表示されているURLを確認し、フォルダのIDを確認・コピーしてください。

Google Apps Script(GAS)で新規プロジェクトを作成して実行する
添付ファイルを保存するためのフォルダをGoogleドライブ上に作成し、フォルダIDを取得したら次はGoogle Apps Script(GAS)での操作に移ります。
以下の手順でGoogle Apps Script(GAS)にプロジェクトを作成してください。
Google Apps Script(GAS)にアクセスし、[新しいプロジェクト]をクリックしてください。

[新しいプロジェクト]をクリックしたら、次は[無題のプロジェクト]をクリックしてください。

[無題のプロジェクト]をクリックすると「プロジェクトの名前を変更」と書かれたウィンドウがポップアップします。
その中にわかりやすい名前を入力してください。
名前を入力したら、右下にある[名前を変更]をクリックしてください。

[名前を変更]をクリックすると、ひとつ前の画面に戻り、プロジェクト名が変更されていることが確認できます。
次に、ソースコード(Gmailの添付ファイルを自動でGoogleドライブに保存するためのGAS)コピーし、右側の枠に貼り付けてください。
function saveEmailAttachmentsToDrive() {
// ユーザー設定 - ここを変更してください
var settings = {
excludedEmailAddresses: ['email@example.com', 'another_email@example.com'], // 処理対象外のメールアドレス
folderId: 'xxxxxxxxxxxxxxxxxxxxxx', // 添付ファイルを保存するGoogleドライブフォルダのID
maxThreads: 30, // 一度に処理するスレッド数
waitTime: 3000, // ファイル保存後の待機時間(ミリ秒)
retryCount: 3 // 保存失敗時の再試行回数
};
var startTime = new Date(); // 処理開始時間の記録
// フォルダ、ログファイル、処理済み添付ファイルID、処理済みスレッドIDの取得
var folder = DriveApp.getFolderById(settings.folderId); // 保存先のGoogleドライブフォルダを取得
var logFile = getLogFile(); // ログファイルを取得または作成
var processedAttachments = getProcessedSet('processedAttachments'); // 既に処理された添付ファイルIDを取得
var processedThreads = getProcessedSet('processedThreads'); // 既に処理されたスレッドIDを取得
var start = getStartIndex(); // 前回の実行位置を取得
try {
var threads = GmailApp.getInboxThreads(start, settings.maxThreads); // 指定された数のスレッドを取得
if (threads.length === 0) { // スレッドが存在しない場合の処理
logMessage(logFile, "全ての添付ファイルを保存しました。");
PropertiesService.getScriptProperties().deleteProperty('start'); // 全スレッドを処理済みの場合、開始位置をリセット
return;
}
threads.forEach(thread => {
if (!processedThreads.has(thread.getId())) { // まだ処理していないスレッドか確認
processThread(thread, settings, folder, processedAttachments, logFile); // スレッド内のメッセージを処理
processedThreads.add(thread.getId()); // 処理済みスレッドIDを記録
}
});
PropertiesService.getScriptProperties().setProperty('start', (start + settings.maxThreads).toString()); // 次回の開始位置を更新
saveProcessedSet('processedAttachments', processedAttachments); // 処理済み添付ファイルIDを保存
saveProcessedSet('processedThreads', processedThreads); // 処理済みスレッドIDを保存
logMessage(logFile, "スレッドの一部を処理しました。次回実行時に続きます。");
} catch (e) {
logError(logFile, "スレッドの処理中にエラーが発生しました: " + e.toString()); // エラーログを記録
} finally {
var endTime = new Date(); // 処理終了時間の記録
var duration = (endTime - startTime) / 1000; // 処理時間を秒単位で計算
logMessage(logFile, `処理時間: ${duration} 秒`); // 処理時間をログに記録
}
}
// スレッド内のメッセージと添付ファイルを処理する関数
function processThread(thread, settings, folder, processedAttachments, logFile) {
thread.getMessages().forEach(message => { // スレッド内の各メッセージを処理
var sender = message.getFrom() || "noname"; // 送信者のメールアドレスを取得、存在しない場合は"noname"
if (settings.excludedEmailAddresses.some(email => sender.includes(email))) return; // 対象外メールアドレスの場合スキップ
var formattedDateTime = formatDateTime(message.getDate()); // メッセージの受信日時をフォーマット
message.getAttachments().forEach((attachment, index) => { // メッセージ内の各添付ファイルを処理
processAttachment(message, attachment, index, sender, formattedDateTime, folder, processedAttachments, logFile, settings.waitTime, settings.retryCount);
});
});
}
// 添付ファイルを処理し、フォルダに保存する関数
function processAttachment(message, attachment, index, sender, formattedDateTime, folder, processedAttachments, logFile, waitTime, retryCount) {
var attachmentId = generateAttachmentId(message, attachment, index); // 添付ファイルIDを生成
if (processedAttachments.has(attachmentId)) return; // 既に処理済みの添付ファイルはスキップ
var uniqueFileName = getUniqueFileName(folder, cleanFileName(`${sender}_${formattedDateTime}_${attachment.getName()}`), message.getId(), index); // 一意のファイル名を生成
var saved = false; // 保存フラグの初期化
for (var attempt = 0; attempt < retryCount; attempt++) { // 再試行回数分ループ
try {
var attachmentFile = folder.createFile(attachment.copyBlob()); // 添付ファイルをフォルダに保存
attachmentFile.setName(uniqueFileName); // ファイル名を設定
processedAttachments.add(attachmentId); // 添付ファイルIDを記録
saved = true; // 保存成功フラグを立てる
break; // 保存に成功したらループを抜ける
} catch (e) {
logError(logFile, `ファイルの保存に失敗しました (試行回数: ${attempt + 1}): ${e.toString()}`); // 保存失敗時のエラーログを記録
Utilities.sleep(waitTime); // 保存失敗時に待機時間を設定
}
}
if (!saved) {
logError(logFile, "ファイルの保存に最終的に失敗しました: " + uniqueFileName); // 保存が最終的に失敗した場合のエラーログを記録
}
}
// 添付ファイルIDを生成する関数(メッセージIDと添付ファイルのハッシュ値を使用)
function generateAttachmentId(message, attachment, index) {
// 添付ファイルの内容をMD5ハッシュ化して一意のIDを生成
var hash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, attachment.getBytes()).map(byte => {
var v = (byte < 0 ? byte + 256 : byte).toString(16); // バイト値を16進数に変換
return v.length == 1 ? "0" + v : v; // 1桁の値は2桁に揃える
}).join("");
return `${message.getId()}_${hash}`; // メッセージIDとハッシュ値を組み合わせて一意のIDを生成
}
// 日付と時間をフォーマットする関数
function formatDateTime(date) {
// 年月日_時分の形式にフォーマット
return `${date.getFullYear().toString().slice(-2)}${String(date.getMonth() + 1).padStart(2, '0')}${String(date.getDate()).padStart(2, '0')}_${String(date.getHours()).padStart(2, '0')}${String(date.getMinutes()).padStart(2, '0')}`;
}
// ファイル名をクリーンアップする関数
function cleanFileName(fileName) {
// ファイル名に使用できない文字を削除
return fileName.replace(/[\\/:*?"<>|]/g, '');
}
// 一意のファイル名を生成する関数
function getUniqueFileName(folder, baseFileName, messageId, index) {
var extension = baseFileName.slice(baseFileName.lastIndexOf('.')); // ファイルの拡張子を取得
var baseName = baseFileName.slice(0, baseFileName.lastIndexOf('.')); // ファイル名のベース部分を取得
var uniqueFileName = `${baseName}_${messageId}_${index}${extension}`; // 一意のファイル名を生成
if (!folder.getFilesByName(uniqueFileName).hasNext()) return uniqueFileName; // ファイル名が重複していない場合そのまま返す
return `${baseName}_${new Date().getTime()}_${messageId}_${index}${extension}`; // ファイル名が重複する場合タイムスタンプを追加して返す
}
// ログファイルを取得または作成する関数
function getLogFile() {
var logFileName = 'email_attachment_log.txt'; // ログファイルの名前
var logFiles = DriveApp.getFilesByName(logFileName); // ログファイルを取得
return logFiles.hasNext() ? logFiles.next() : DriveApp.createFile(logFileName, ''); // 既存のログファイルがあればそれを使用、なければ新規作成
}
// エラーログを記録する関数
function logMessage(logFile, message) {
var logContent = logFile.getBlob().getDataAsString(); // ログファイルの内容を取得
logFile.setContent(logContent + `${new Date().toLocaleString()} - ${message}\n`); // 新しいログメッセージを追加して保存
}
// エラーログを記録する関数
function logError(logFile, message) {
logMessage(logFile, `ERROR: ${message}`); // エラーログを記録
}
// 処理済みIDセットを取得する関数
function getProcessedSet(property) {
var processedSet = PropertiesService.getUserProperties().getProperty(property); // プロパティから処理済みIDセットを取得
return processedSet ? new Set(JSON.parse(processedSet)) : new Set(); // JSONからセットに変換して返す
}
// 処理済みIDセットを保存する関数
function saveProcessedSet(property, processedSet) {
PropertiesService.getUserProperties().setProperty(property, JSON.stringify(Array.from(processedSet))); // セットをJSONに変換して保存
}
// 開始位置を取得する関数
function getStartIndex() {
var start = PropertiesService.getScriptProperties().getProperty('start'); // 前回の実行位置を取得
return start ? parseInt(start, 10) : 0; // 数値に変換して返す、存在しない場合は0を返す
}
ソースコードを貼り付けたら左上にある[保存]をクリックしてください。

次に、Googleドライブで保存先のフォルダを作成し、フォルダIDを取得するで取得したフォルダIDを以下xxxxxxxxxxxxxxの場所に貼り付けてください。
folderId: 'xxxxxxxxxxxxxx', // 添付ファイルを保存するGoogleドライブフォルダのID
貼り付けると以下のようになります。
folderId: '1hjnn0L64MM9ZSqKjtW5BDjtP8BaQkeKN', // 添付ファイルを保存するGoogleドライブフォルダのID
フォルダIDを貼り付けたら再度[保存]をクリックしてください。

プロジェクトを保存したら、次は左上にある[▶ 実行]をクリックしてください。

[▶ 実行]をクリックすると「承認が必要です」と書かれたウィンドウがポップアップします。
[権限を確認]をクリックしてください。

[権限を確認]をクリックすると「アカウントの選択」と書かれたウィンドウが開きます。
ここではこのGoogle Apps Script(GAS)を使用するアカウント(添付ファイルを自動で保存したいGmailアカウント)を選択してください。

アカウントを選択すると「このアプリはGoogleで確認されていません」と書かれた画面に移動します。
左下にある[詳細]をクリックしてください。

[詳細]をクリックすると画面が展開されます。
その中にある[(安全ではないページ)に移動]をクリックしてください。

[(安全ではないページ)に移動]をクリックすると「Googleアカウントへのアクセスをリクエスしています」と表示されます。
一番下までスクロールし、[許可]をクリックしてください。

[許可]をクリックすると、Gmailの添付ファイルを自動でGoogleドライブの指定したフォルダに保存するスクリプトが実行されます。

実行中にGoogleドライブの指定したフォルダを開くと、添付ファイルがどんどん保存されているこが確認できます。

トリガー機能を使って定期的に自動実行されるように設定する
最後に、Google Apps Script(GAS)のトリガー機能を使って、このスクリプトが定期的に自動実行されるように設定していきます。
毎回手動で実行ボタンを押しても問題はありませんが、トリガー機能を使うことで、以下のようなメリットがあります。
- 自動化の実現:トリガー機能を設定することで、スクリプトを自動的に実行できます。これにより、手動でスクリプトを実行する手間を省き、定期的なタスクを自動化することができます。例えば、1時間ごとにスクリプトを実行する設定をすれば、新しい添付ファイルが自動的にGoogleドライブに保存されるようになります。
- オンラインでの実行:トリガーはGoogleのサーバー上で実行されるため、パソコンが常にオンになっている必要がありません。初期設定さえ終わらせておけば決まった時間に必ず新しい添付ファイルが自動的にGoogleドライブに保存されるようになります。
ここからは、トリガー機能を使って定期的にスクリプトが自動実行されるように設定する方法を、実際の画面ももちいながらわかりやすく解説していきます。
Google Apps Script(GAS)にアクセスし、自動実行したいGoogle Apps Script(GAS)を開いて[トリガー]をクリックしてください。

[トリガー]をクリックすると、トリガーの設定画面に移動します。
右下にある[トリガーの追加]をクリックしてください。

[トリガーの追加]をクリックすると、どのようなトリガー(きっかけ)でスクリプトを実行するか選択する画面が表示されます。
今回はGmailの添付ファイルを自動でGoogleドライブに保存するためのスクリプトを1時間毎に自動で実行したいため、一番したにある「時間の間隔を選択(時間)」で[1時間おき]を選択し、右下にある[保存]をクリックしてください。

[保存]をクリックすると「Choose an account」と書かれたウィンドウが立ち上がります。
スクリプトを自動実行するGoogleアカウントを選択してください。

スクリプトを自動実行するGoogleアカウントを選択すると「Google hasn't verified this app」と書かれた画面に切り替わります。
その中にある[Advanced]をクリックしてください。

[Advanced]をクリックすると、画面が下に向かって展開されます。
その中にある[Go To…]をクリックしてください。

次に[Allow]をクリックしてください。

[Allow]をクリックすると、トリガーの設定画面に戻り、トリガーが設定されていることが確認できます。
これでGmailの添付ファイルを自動保存するスクリプトが1時間おきに実行されます。

スクリプトの主な機能と中身
ここからは、ご紹介したスクリプトの主な機能と中身をご紹介します。
このスクリプトはGmailの受信トレイを見て、新しいメールが来ていないか確認し、そのメールに添付されているファイルをGoogleドライブに自動的に保存してくれます。また、特定の送り主のメールや、すでに保存したファイルを再度保存しないようにしてくれる機能も持っています。
編集可能な箇所
まずはユーザーごとに編集可能な箇所からご紹介します。
// ユーザー設定 - ここを変更してください
var settings = {
excludedEmailAddresses: ['email@example.com', 'another_email@example.com'], // 処理対象外のメールアドレス
folderId: '1hjnn0L64MM9ZSqKjtW5BDjtP8BaQkeKN', // 添付ファイルを保存するGoogleドライブフォルダのID
maxThreads: 30, // 一度に処理するスレッド数
waitTime: 3000, // ファイル保存後の待機時間(ミリ秒)
retryCount: 3 // 保存失敗時の再試行回数
};
先頭のこの部分はユーザーの環境に応じて編集が可能です。
処理対象外のメールアドレスを指定する
例えば、自分が送信したメールの添付ファイルは保存したくない場合や、特定の人からのメールの添付ファイルを保存したくない場合は以下のアドレスを編集してください。
excludedEmailAddresses: ['email@example.com', 'another_email@example.com'], // 処理対象外のメールアドレス
GoogleドライブのフォルダID
添付ファイルを保存するためのフォルダIDは以下の部分で変更可能です。
folderId: '1hjnn0L64MM9ZSqKjtW5BDjtP8BaQkeKN', // 添付ファイルを保存するGoogleドライブフォルダのID
一度に処理するメールの件数
受信するメールが非常に多い場合はmaxThreadsの数字を変更してください。
数字を大きくすることで1回に処理できるメールの件数が増えます。
maxThreads: 30, // 一度に処理するスレッド数
保存される添付ファイルの名前について
送信者が noreply@example.com、受信日時が 2024年6月29日13時25分、元のファイル名が 請求書.pdf で、メッセージIDが 123456789、インデックスが 0 の場合、以下のように変換して保存しています。
noreply@example.com_240629_1325_請求書_123456789_0.pdf
この変換により同じファイルを重複して保存しないように制限しています。
エラー処理
ファイルの保存に失敗した場合、再試行を行い、問題が解決しない場合はエラーメッセージをログに記録します。
ログはemail_attachment_log.txt
という名前でGoogleドライブ上に自動的に作成されます。
このログファイルはどんどん大きくなるため、定期的に削除してください。
※ファイルごと削除してもまた自動で作成されます。
重複処理の防止
このプログラムは、同じメールや添付ファイルを二度保存しないように工夫されています。
これは、メールのスレッドIDと添付ファイルIDを保存することで実現しています。
まず、スレッドIDについて説明します。
Gmailのメールスレッドには一意のIDが付与されています。
このプログラムは、処理したスレッドIDを保存しておき、次回の実行時に同じスレッドIDが含まれていないか確認します。
もし、スレッドIDが保存済みであれば、そのスレッドはスキップされます。
これにより、同じスレッドのメールが再度処理されることを防ぎます。
次に、添付ファイルIDについて説明します。
添付ファイルIDは、メッセージIDと添付ファイルの内容を使って一意に生成されます。
具体的には、添付ファイルの内容をMD5という方法でハッシュ化し、そのハッシュ値とメッセージIDを組み合わせてIDを作成します。
このIDも保存しておき、次回の実行時に確認します。同じIDが保存されていれば、その添付ファイルはスキップされます。
これにより、同じ内容のファイルが再度保存されることを防ぎます。
このように、スレッドIDと添付ファイルIDを管理することで、重複して処理することを防止しています。これにより、同じメールやファイルが何度も処理されることがなくなります。
よくある質問
その他Googleサービスに関する記事
その他Googleサービスに関する記事はこちらです。ぜひご覧ください。
-
iPhoneでWebサイトをPC用ページ(デスクトップ表示)で開く方法
こんな人にオススメの記事です iPhoneでPC向けのページを見たい方 iPhoneを使っている方 iPhoneでChromeを使っている方 このページでは、iPhoneでWebサイトをスマホ用表... -
Google ChromeのブックマークをHTMLファイルにエクスポートする方法
こんな人にオススメの記事です Google chromeのブックマークを他のパソコンに移動したい人 Google chromeのブックマークのバックアップを取っておきたい人 Google chrom... -
添付されているwinmail.datの開き方6選
こんな方にオススメ winmail.datが添付されたメールが来て困っている方 winmail.datが何なのかわからない方 winmail.datの開き方を知りたい方 winmail.datを変換したい... -
Internet Explorerのサポート終了に伴いおさえておきたい設定まとめ
Internet Explorerのサポートが2022年6月15日(日本時間で2022年6月16日)に終了したことに伴い、おさえていきたい設定や操作をまとめてご紹介します。 ここで紹介して... -
iPhoneでページ内検索をする方法 Safari・Chrome編
このページでは、iPhoneのSafariとiPhoneアプリのGoogle Chromeでウェブサイトの中の文字を検索(ページ内検索)する方法を詳しく解説していきます。 パソコンのインタ... -
【Gmail】添付ファイルをまとめてダウンロードする方法
こんな人にオススメの記事です 受信したメールに多数の添付ファイルがついている 添付ファイルを1個1個ダウンロードするのが面倒な人 複数のメールの添付ファイルを一... -
【Google日本語入力】変換候補から絵文字を削除する方法
こんな人にオススメの記事です 作業効率を重視し、絵文字の候補表示が不要または邪魔と感じる人。 絵文字をあまり使用せず、シンプルな入力を好む人。 正確で迅速な日本... -
これで完璧!Gmailで迷惑メールの設定を解除する手順
こんな人にオススメの記事です Gmailで必要なメールが迷惑メールに振り分けられてしまう人 Gmailで迷惑メールを何度解除しても迷惑メールフォルダーに振り分けられてし... -
【Windows11】Gmailの受信通知をデスクトップに出す方法
こんな人にオススメの記事です Gmailからの通知が来なくて困っている人 Gmailの受信通知をデスクトップに表示させたい人 一刻も早くメールの受信を知りたい人 この記事...
最後までご覧いただきありがとうございました。
このサイトは情シスマンが半径3m以内のITに関する情報を掲載してるサイトです。
Windows系を主として、ソフトや周辺機器の操作や設定について画像も用いながらわかりやすく解説しています。
解説している内容に誤りがあった場合や、補足が必要な場合、もっと知りたい場合はこちらのお問い合わせフォームからご連絡ください。
個人の方を限定にサポートさせていただきます。
実行環境
Windows11 Home 24H2
64 ビット オペレーティング システム
11th Gen Intel(R) Core(TM) i7-11375H @ 3.30GHz 3.30 GHz
16.0 GB RAM
Microsoft 365
コメント
コメント一覧 (2件)
初めてコメントします。
職場の複合機入替えに伴い、受信FAXがメールで飛んでくるようになったので、
こちらの記事を参考にファイル保存をするように設定したのですが、
どんどん昔のファイルを保存しており、
最新のファイルが保存されていないのですが、
なにか設定ありますでしょうか?
また、指定アドレスからのメールだけ処理する方法もありましたら、
御教示いただきたいです。
宜しくお願い致します。
以下に、Gmailの添付ファイルをGoogleドライブに保存するスクリプト全体を記載します。
最新のファイルを優先的に処理し、特定のアドレスからのメールのみ処理する機能を組み込んでいます。
------------------------------------------------------------------------------------
function saveEmailAttachmentsToDrive() {
// ユーザー設定 - 必要に応じて変更してください
var settings = {
senderEmail: 'specific_sender@example.com', // 処理対象の送信者(特定しない場合は null または空文字に設定)
folderId: 'xxxxxxxxxxxxxxxxxxxxxx', // 添付ファイルを保存するGoogleドライブフォルダのID
maxThreads: 30, // 一度に処理するスレッド数
waitTime: 3000, // ファイル保存後の待機時間(ミリ秒)
retryCount: 3 // 保存失敗時の再試行回数
};
var folder = DriveApp.getFolderById(settings.folderId); // 保存先フォルダを取得
var logFile = getLogFile(); // ログファイルを取得または作成
var processedAttachments = getProcessedSet('processedAttachments'); // 既に処理済みの添付ファイルIDを取得
var processedThreads = getProcessedSet('processedThreads'); // 既に処理済みのスレッドIDを取得
try {
// 特定の送信者からのメールを処理するか、全体を処理するかを選択
var threads = settings.senderEmail
? getThreadsFromSpecificSender(settings.senderEmail, settings.maxThreads)
: getLatestThreads(settings.maxThreads);
// スレッドを処理
threads.forEach(thread => {
if (!processedThreads.has(thread.getId())) {
processThread(thread, settings, folder, processedAttachments, logFile);
processedThreads.add(thread.getId()); // 処理済みスレッドIDを記録
}
});
// 処理済み情報を保存
saveProcessedSet('processedAttachments', processedAttachments);
saveProcessedSet('processedThreads', processedThreads);
logMessage(logFile, "指定スレッドの処理が完了しました。");
} catch (e) {
logError(logFile, "スレッド処理中にエラー: " + e.toString());
}
}
// 特定の送信者からのスレッドを取得
function getThreadsFromSpecificSender(senderEmail, maxThreads) {
var query = `from:${senderEmail}`; // Gmail検索クエリ
return GmailApp.search(query, 0, maxThreads);
}
// 最新のスレッドを取得
function getLatestThreads(maxThreads) {
return GmailApp.getInboxThreads(0, maxThreads).sort((a, b) => {
return b.getLastMessageDate() - a.getLastMessageDate(); // 最新の日付順にソート
});
}
// スレッド内のメッセージを処理
function processThread(thread, settings, folder, processedAttachments, logFile) {
thread.getMessages().forEach(message => {
var sender = message.getFrom() || "noname"; // 送信者情報を取得
if (settings.senderEmail && !sender.includes(settings.senderEmail)) return; // 設定と一致しない場合スキップ
var formattedDateTime = formatDateTime(message.getDate()); // メッセージの日時をフォーマット
message.getAttachments().forEach((attachment, index) => {
processAttachment(message, attachment, index, sender, formattedDateTime, folder, processedAttachments, logFile, settings.waitTime, settings.retryCount);
});
});
}
// 添付ファイルを保存
function processAttachment(message, attachment, index, sender, formattedDateTime, folder, processedAttachments, logFile, waitTime, retryCount) {
var attachmentId = generateAttachmentId(message, attachment, index);
if (processedAttachments.has(attachmentId)) return; // 既に処理済みの場合スキップ
var uniqueFileName = getUniqueFileName(folder, cleanFileName(`${sender}_${formattedDateTime}_${attachment.getName()}`), message.getId(), index);
var saved = false;
for (var attempt = 0; attempt < retryCount; attempt++) { try { var attachmentFile = folder.createFile(attachment.copyBlob()); attachmentFile.setName(uniqueFileName); processedAttachments.add(attachmentId); saved = true; break; } catch (e) { logError(logFile, `ファイル保存失敗 (試行: ${attempt + 1}): ${e.toString()}`); Utilities.sleep(waitTime); } } if (!saved) logError(logFile, `最終的に保存失敗: ${uniqueFileName}`); } // ファイル名を一意にする function getUniqueFileName(folder, baseFileName, messageId, index) { var extension = baseFileName.slice(baseFileName.lastIndexOf('.')); var baseName = baseFileName.slice(0, baseFileName.lastIndexOf('.')); var uniqueFileName = `${baseName}_${messageId}_${index}${extension}`; if (!folder.getFilesByName(uniqueFileName).hasNext()) return uniqueFileName; return `${baseName}_${new Date().getTime()}_${messageId}_${index}${extension}`; } // 日付フォーマット function formatDateTime(date) { return `${date.getFullYear()}${String(date.getMonth() + 1).padStart(2, '0')}${String(date.getDate()).padStart(2, '0')}_${String(date.getHours()).padStart(2, '0')}${String(date.getMinutes()).padStart(2, '0')}`; } // ファイル名をクリーンアップ function cleanFileName(fileName) { return fileName.replace(/[\\/:*?"<>|]/g, '');
}
// 添付ファイルID生成
function generateAttachmentId(message, attachment, index) {
var hash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, attachment.getBytes()).map(byte => {
var v = (byte < 0 ? byte + 256 : byte).toString(16); return v.length == 1 ? "0" + v : v; }).join(""); return `${message.getId()}_${hash}`; } // 処理済みセット取得 function getProcessedSet(property) { var processedSet = PropertiesService.getUserProperties().getProperty(property); return processedSet ? new Set(JSON.parse(processedSet)) : new Set(); } // 処理済みセット保存 function saveProcessedSet(property, processedSet) { PropertiesService.getUserProperties().setProperty(property, JSON.stringify(Array.from(processedSet))); } // ログファイル取得 function getLogFile() { var logFileName = 'email_attachment_log.txt'; var logFiles = DriveApp.getFilesByName(logFileName); return logFiles.hasNext() ? logFiles.next() : DriveApp.createFile(logFileName, ''); } // ログ記録 function logMessage(logFile, message) { var logContent = logFile.getBlob().getDataAsString(); logFile.setContent(logContent + `${new Date().toLocaleString()} - ${message}\n`); } // エラーログ記録 function logError(logFile, message) { logMessage(logFile, `ERROR: ${message}`); }