如何自动将 Gmail 附件保存到 Google 云端硬盘
本文推荐给以下人群:
- 想要自动整理和保存电子邮件附件的人
- 每天收到大量附件的人
- 想要更方便地使用 Gmail 的人
当您每天使用 Gmail 时,经常会收到许多附件。
特别是对于独资经营者和自由职业者来说,保持这些文件的组织和安全非常重要。
但每次都要手动保存比较麻烦,而且还有误删除的风险。
此外,随着您收到的电子邮件数量增加,您的 Google 帐户可能会满。
本文将向您展示如何使用 Google Apps 脚本 (GAS) 自动将 Gmail 附件保存到 Google 云端硬盘。
还包含源代码,因此任何人都可以按照本文中的步骤轻松设置它。
目次
需要准备什么
首先,让我们看一下实现这种自动化所需的工具和帐户。
- Gmail 帐户:用于接收附件的 Gmail 帐户
- 谷歌云端硬盘帐户:Google Drive 保存文件
- Google Apps脚本(GAS):Google提供的脚本环境
Google Apps Script (GAS) 是一个脚本环境,用于自动化各种 Google 服务(Gmail、Google Drive、Google 日历等)。即使您没有编程经验,也可以使用简单的脚本自动执行任务。
在 Google Drive 中创建目标文件夹并获取文件夹 ID
首先,按照以下步骤将其保存到 Google 云端硬盘。创建一个文件夹来保存附件死亡,获取文件夹ID请。
- 打开 Google 云端硬盘并创建一个要保存附件的文件夹。
- 打开创建的文件夹
- 检查浏览器地址栏中显示的 URL 并获取文件夹 ID
例: https://drive.google.com/drive/u/0/folders/XXXXXXXXXXXXXXXXX
上述 URL 的“XXXXXXXXXXXXXXXXXX”部分是文件夹 ID。
步打开 Google 云端硬盘并创建一个要保存附件的文件夹。
谷歌云端硬盘打开右键单击空白处死亡,点击【新建文件夹】请。
当您单击[新建文件夹]时,将弹出一个显示“新建文件夹”的屏幕。
在其中输入描述性名称并单击“创建”请。
在本文中,为了便于理解,我们将使用“Gmail”。
在 Google Drive 中创建目标文件夹并获取文件夹 ID
步打开创建的文件夹
单击 [创建] 将返回到上一屏幕,显示您创建的文件夹。
请打开该文件夹。
在 Google Drive 中创建目标文件夹并获取文件夹 ID
步检查/复制文件夹 ID
检查浏览器地址栏中显示的 URL,检查/复制文件夹 ID请。
例: https://drive.google.com/drive/u/0/folders/XXXXXXXXXXXXXXXXX
上述 URL 的“XXXXXXXXXXXXXXXXXX”部分是文件夹 ID。
在 Google Drive 中创建目标文件夹并获取文件夹 ID
使用 Google Apps 脚本 (GAS) 创建并运行新项目
保存附件的文件夹谷歌云端硬盘创建完上面的文件夹并获取文件夹ID后,我们将继续使用Google Apps脚本(GAS)进行操作。
按照以下步骤在 Google Apps 脚本 (GAS) 中创建项目。
步访问 Google Apps 脚本 (GAS) 并单击“新建项目”
Google Apps脚本(GAS)访问单击新建项目请。
使用 Google Apps 脚本 (GAS) 创建并运行新项目
步将项目重命名为更有意义的名称
单击“新建项目”后,下一步是单击[无标题项目]请。
使用 Google Apps 脚本 (GAS) 创建并运行新项目
单击“无标题项目”,将弹出一个窗口,显示“重命名项目”。
在其中输入描述性名称请。
在本例中,我们将选择“Gmail自动保存附件”。
输入名字后,在右下角单击重命名请。
使用 Google Apps 脚本 (GAS) 创建并运行新项目
步粘贴源代码并单击“保存”
单击[重命名]返回上一屏幕并确认项目名称已更改。
下一个,复制源代码(GAS用于自动将Gmail附件保存到Google Drive)并将其粘贴到右侧框架中请。
请删除默认代码后粘贴。
function saveEmailAttachmentsToDrive() {
// ユーザー設定 - ここを変更してください
var settings = {
excludedEmailAddresses: ['', ''], // 処理対象外のメールアドレス
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 Apps 脚本 (GAS) 创建并运行新项目
步粘贴 Google Drive 文件夹 ID,然后单击 [保存]
下一个,在 Google Drive 中创建目标文件夹并获取文件夹 ID获得与将文件夹 ID 粘贴到 xxxxxxxxxxxxx 下面的位置请。
folderId: 'xxxxxxxxxxxxxx', // 添付ファイルを保存するGoogleドライブフォルダのID
粘贴后,它将如下所示:
folderId: '1hjnn0L64MM9ZSqKjtW5BDjtP8BaQkeKN', // 添付ファイルを保存するGoogleドライブフォルダのID
粘贴文件夹 ID 并重试单击“保存”请。
使用 Google Apps 脚本 (GAS) 创建并运行新项目
步单击[▶运行]
保存项目后,接下来是左上角单击 [▶ 运行]请。
使用 Google Apps 脚本 (GAS) 创建并运行新项目
步点击【检查权限】
单击 [▶ 运行],将弹出一个窗口,显示“需要授权”。
点击【检查权限】请。
使用 Google Apps 脚本 (GAS) 创建并运行新项目
步选择要使用的 Google 帐户
当您单击[检查权限]时,将打开一个标有“选择帐户”的窗口。
这里这个选择使用 Google Apps 脚本 (GAS) 的帐户(您要为其自动保存附件的 Gmail 帐户)请。
使用 Google Apps 脚本 (GAS) 创建并运行新项目
步点击【详情】
当您选择一个帐户时,您将被重定向到一个屏幕,显示“此应用程序尚未经过 Google 验证”。
显示此消息是因为我创建了自己的 Google Apps 脚本 (GAS)。
在左下角点击【详情】请。
使用 Google Apps 脚本 (GAS) 创建并运行新项目
步点击【前往(不安全页面)】
单击[详细信息]以展开屏幕。
在其中点击【前往(不安全页面)】请。
使用 Google Apps 脚本 (GAS) 创建并运行新项目
步单击[允许]
当我点击[转到(不安全页面)]时,会显示一条消息“请求访问您的 Google 帐户”。
滚动到底部并单击[允许]请。
如果您允许,您的 Gmail 地址将收到一封电子邮件,内容为:“您已被授予访问您的 Google 帐户以保存 Gmail 自动保存附件的权限。”
使用 Google Apps 脚本 (GAS) 创建并运行新项目
步确认自动将 Gmail 附件保存到 Google Drive 中指定文件夹的脚本已执行。
单击“允许”将运行一个脚本,该脚本会自动将 Gmail 附件保存到 Google 云端硬盘中的指定文件夹。
使用 Google Apps 脚本 (GAS) 创建并运行新项目
步验证附件是否保存在 Google 云端硬盘的指定文件夹中
如果您在运行时打开 Google 云端硬盘中的指定文件夹,您可以看到正在保存附件。
了解更多详情关于引入脚本的内容正如 中所解释的,它是一种一次检查最新 30 封电子邮件并保存附件的机制。
如果 30 项中没有附件,则不会保存任何内容。
使用 Google Apps 脚本 (GAS) 创建并运行新项目
使用触发功能将其设置为定期自动运行
最后,我们将使用Google Apps Script(GAS)的触发功能来设置此脚本定期自动运行。
每次手动按下执行按钮没有问题,但使用触发功能有以下优点。
- 实现自动化:通过设置触发功能可以自动执行脚本。这使您可以自动执行日常任务,而无需手动运行脚本。例如,您可以将脚本设置为每小时运行一次,以便新附件自动保存到 Google 云端硬盘。
- 在线执行:触发器在 Google 的服务器上运行,因此您的计算机不需要一直处于开启状态。完成初始设置后,新附件将在指定时间自动保存到 Google 云端硬盘。
从这里开始,我们将使用实际屏幕以易于理解的方式解释如何使用触发功能设置定期自动执行的脚本。
不是必需的,所以如果不需要自动执行则无需设置。
步打开您想要自动运行的Google Apps脚本(GAS),然后单击[触发器]
Google Apps脚本(GAS)转至 ,打开您想要自动运行的 Google Apps 脚本 (GAS),然后点击【触发】请。
使用触发功能将其设置为定期自动运行
步单击添加触发器
点击【触发】,进入触发设置界面。
在右下角单击添加触发器请。
使用触发功能将其设置为定期自动运行
步在“选择时间间隔(小时)”中选择“每小时”,然后点击右下角的“保存”。
当您单击[添加触发器]时,将出现一个屏幕,您可以在其中选择要使用哪种触发器来运行脚本。
这次,我想自动运行一个脚本,每小时自动将 Gmail 附件保存到 Google Drive,所以我首先编写它。在“选择时间间隔(小时)”中选择“每小时”,然后点击右下角的“保存”。请。
使用触发功能将其设置为定期自动运行
步选择Google账户自动运行脚本
当您单击[保存]时,将出现一个显示“选择帐户”的窗口。
参见:
选择Google账户自动运行脚本请。
使用触发功能将其设置为定期自动运行
步点击【高级】
当您选择 Google 帐户自动运行脚本时,您将看到一个屏幕,显示“Google 尚未验证此应用程序”。
在其中点击【高级】请。
使用触发功能将其设置为定期自动运行
步单击[转到...]
单击“高级”,屏幕将向底部展开。
在其中单击[转到...]请。
使用触发功能将其设置为定期自动运行
步单击[允许]
下一个单击[允许]请。
使用触发功能将其设置为定期自动运行
步确保已设置触发器
单击[允许]返回触发设置屏幕并确认触发已设置。
自动保存 Gmail 附件的脚本现在每小时运行一次。
使用触发功能将其设置为定期自动运行
脚本主要功能及内容
下面我们就介绍一下引入脚本的主要功能和内容。
该脚本会查看您的 Gmail 收件箱,检查新电子邮件,并自动将所有附件保存到 Google 云端硬盘。它还具有防止再次保存来自特定发件人的电子邮件和已保存的文件的功能。
可编辑部分
首先,我们将介绍每个用户可以编辑的部分。
// ユーザー設定 - ここを変更してください
var settings = {
excludedEmailAddresses: ['', ''], // 処理対象外のメールアドレス
folderId: '1hjnn0L64MM9ZSqKjtW5BDjtP8BaQkeKN', // 添付ファイルを保存するGoogleドライブフォルダのID
maxThreads: 30, // 一度に処理するスレッド数
waitTime: 3000, // ファイル保存後の待機時間(ミリ秒)
retryCount: 3 // 保存失敗時の再試行回数
};
开头的这部分可以根据用户的环境进行编辑。
指定不受处理的电子邮件地址
例如,如果您不想保存已发送电子邮件的附件,或者不想保存特定人员发送的电子邮件的附件,请编辑以下地址。
excludedEmailAddresses: ['', ''], // 処理対象外のメールアドレス
Google 云端硬盘文件夹 ID
可以在以下部分中更改用于保存附加文件的文件夹 ID。
folderId: '1hjnn0L64MM9ZSqKjtW5BDjtP8BaQkeKN', // 添付ファイルを保存するGoogleドライブフォルダのID
一次处理的电子邮件数量
如果您收到大量电子邮件,请更改 maxThreads 数量。
增加数量将增加一次可以处理的电子邮件数量。
即使我将其设置为 999 项,也会超时。
这会受到附加文件大小的影响,但将数字设置为 70 或更少是安全的。
maxThreads: 30, // 一度に処理するスレッド数
关于保存的附件文件的名称
发件人,若接收日期时间为2024年6月29日13:25,原文件名为invoice.pdf,消息ID为123456789,索引为0,则转换并保存如下。
_240629_1325_请求书_123456789_0.pdf
此转换可防止您保存同一文件两次。
错误处理
如果保存文件失败,它将重试并记录错误消息(如果问题仍然存在)。
日志是email_attachment_log.txt它将自动在 Google Drive 上创建并使用该名称。
该日志文件会不断增长,因此请定期删除它。
*即使删除整个文件,它也会自动重新创建。
防止重复处理
该程序旨在避免重复保存同一电子邮件或附件。
这是通过存储电子邮件线程 ID 和附件 ID 来实现的。
首先,我们来谈谈线程ID。
Gmail 电子邮件线程被分配了一个唯一的 ID。
该程序保存处理后的线程ID,并在下次执行时检查是否包含相同的线程ID。
如果线程 ID 已保存,则该线程将被跳过。
这可以防止同一线程中的电子邮件再次被处理。
接下来,我们来谈谈附件 ID。
附件ID是使用消息ID和附件内容唯一生成的。
具体来说,使用MD5的方法对附件的内容进行散列,并通过组合散列值和消息ID来创建ID。
也保存此 ID,并在下次运行时检查它。如果存储了相同的 ID,则该附件将被跳过。
这可以防止再次保存具有相同内容的文件。
这样,通过管理线程 ID 和附件文件 ID,我们可以防止重复处理。这可以防止同一电子邮件或文件被多次处理。
常问问题
这个脚本是如何工作的?
此脚本从您的 Gmail 收件箱检索电子邮件,并将其中包含的附件保存到指定的 Google 云端硬盘文件夹中。保存线程 ID 和附件 ID 以防止重复处理。
我应该在哪里设置附件的目标文件夹?
将“folderId”值设置为要保存到的 Google 云端硬盘文件夹的 ID。该 ID 是文件夹 URL 的一部分(例如,https://drive.google.com/drive/folders/folder ID)。
如何排除来自特定电子邮件地址的电子邮件?
将您要排除的电子邮件地址添加到“excludedEmailAddresses”。来自此处所写地址的电子邮件将不会被处理。
我可以更改一次处理的电子邮件数量吗?
是的,您可以通过更改“maxThreads”值来设置一次处理的电子邮件数量。例如,设置 maxThreads: 50 将一次处理 50 个线程。
附件保存时的文件名是什么?
附件将转换为包含发件人电子邮件地址、接收日期和时间、原始文件名、邮件 ID 和附件索引的名称。
如何防止同一个附件被多次保存?
该脚本会保存处理后的线程 ID 和附件 ID,因此不会再次处理相同的附件。
在哪里可以看到脚本的错误日志?
错误日志记录在 Google 云端硬盘的 email_attachment_log.txt 文件中。该文件包含处理期间发生的任何错误的详细信息。
如果运行脚本时保存失败怎么办?
设置重试次数,最多尝试 3 次,直到保存成功。如果失败,将会记录在错误日志中。
我可以在附件名称中包含电子邮件的主题和正文吗?
当前脚本的文件名中不包含主题或正文,但您可以通过修改代码来实现。例如,您可以使用 message.getSubject() 获取主题并将其附加到文件名中。
是否可以只保存某些文件格式?
是的,可以通过在 processAttachment 函数中添加条件来检查文件格式。例如,要仅保存 PDF 文件,请添加 if (attachment.getContentType() === ‘application/pdf’) 等条件。
有没有办法重置已处理的附件 ID?
要重置已处理的附件 ID,请在 Google Apps 脚本编辑器中运行 PropertiesService.getUserProperties().deleteProperty(‘processedAttachments’)。
我可以将此脚本用于多个 Gmail 帐户吗?
这可以通过为每个 Gmail 帐户单独设置脚本来实现。在每个帐户中创建一个 Google Apps 脚本项目并配置该脚本。
是否可以通过电子邮件接收脚本执行结果的通知?
是的,您可以在脚本末尾使用“MailApp.sendEmail”来通过电子邮件通知您执行结果。这使您可以立即知道流程何时完成或是否发生错误。
我可以更改已保存文件的命名约定吗?
是的,您可以通过修改“getUniqueFileName”函数来自定义文件命名约定。例如,您可以更改文件名以包含主题。
有关其他 Google 服务的文章
单击此处查看有关其他 Google 服务的文章。请看一下。
