Apps Script时间驱动触发器不运行,手动执行脚本正常
问题描述
手动执行backupAll()函数可正常备份Google文档、表格、幻灯片,但设置时间驱动触发器后,触发器显示已运行却未生成任何备份文件。已确认项目时区设置正确,更换多个执行时间后问题依旧。
核心问题分析
1. 无人值守环境下的用户身份判断失效
触发器以后台模式运行时,Session.getActiveUser().getEmail()会返回空值,导致代码中file.getOwner().getEmail() == Session.getActiveUser().getEmail()的判断条件永远不成立,直接跳过所有文件的备份逻辑。
2. MIME类型键的类型不匹配
原代码通过Object.keys(NATIVE_MIME_TYPES)获取备份类型数组,返回的是字符串类型(如"application/vnd.google-apps.document"),但DriveApp.getFilesByType()要求传入MimeType枚举值,部分环境下类型不匹配会导致无法获取目标文件。
3. OAuth权限范围不足
触发器运行时,ScriptApp.getOAuthToken()可能未获取到Drive API导出接口的权限,导致导出文件请求失败。
修复方案
步骤1:替换用户身份判断逻辑
将Session.getActiveUser().getEmail()替换为你自己的邮箱字符串,避免无人值守环境下的身份获取限制。
步骤2:修正MIME类型数组定义
直接使用MimeType枚举值数组,确保与DriveApp.getFilesByType()的参数类型匹配。
步骤3:显式声明OAuth权限
在脚本开头添加权限注释,确保触发器能获取到足够的API访问权限。
步骤4:添加调试日志
在关键请求处添加异常静默和日志记录,便于排查触发器运行时的错误。
修改后的完整脚本
// 显式声明所需权限,确保触发器能获取足够权限 // @OnlyCurrentDoc // @OAuthScope https://www.googleapis.com/auth/drive // @OAuthScope https://www.googleapis.com/auth/drive.file var BACKUP_FOLDER_ID = '16SGkgNCPeNn9DgB9RmhLAv-z5C_kCbCM'; var NATIVE_MIME_TYPES = {}; NATIVE_MIME_TYPES[MimeType.GOOGLE_DOCS] = MimeType.MICROSOFT_WORD; NATIVE_MIME_TYPES[MimeType.GOOGLE_SHEETS] = MimeType.MICROSOFT_EXCEL; NATIVE_MIME_TYPES[MimeType.GOOGLE_SLIDES] = MimeType.MICROSOFT_POWERPOINT; var NATIVE_EXTENSIONS = {}; NATIVE_EXTENSIONS[MimeType.GOOGLE_DOCS] = '.docx'; NATIVE_EXTENSIONS[MimeType.GOOGLE_SHEETS] = '.xlsx'; NATIVE_EXTENSIONS[MimeType.GOOGLE_SLIDES] = '.pptx'; // 直接使用MimeType枚举值数组,避免类型不匹配 var BACKUP_MIME_TYPES = [MimeType.GOOGLE_DOCS, MimeType.GOOGLE_SHEETS, MimeType.GOOGLE_SLIDES]; function backupAll() { const backupFolder = DriveApp.getFolderById(BACKUP_FOLDER_ID); // 替换为你的邮箱地址 const currentUserEmail = 'your-email@example.com'; BACKUP_MIME_TYPES.forEach(function(mimeType) { var files = DriveApp.getFilesByType(mimeType); while (files.hasNext()) { var file = files.next(); if (file.getOwner() && file.getOwner().getEmail() === currentUserEmail) { backup(file, backupFolder); } } }); } function backup(file, folder) { var targetName = file.getName() + ' ' + file.getId(); var lastUpdated = file.getLastUpdated(); var pdf = getPdfBlob(file); var native = getNativeBlob(file); var zip = Utilities.zip([pdf, native], targetName + '.zip'); createOrUpdateFileForBlob(zip, folder, lastUpdated); } function createOrUpdateFileForBlob(blob, folder, ifOlderThan) { var existingFiles = folder.getFilesByName(blob.getName()); if (existingFiles.hasNext()) { var file = existingFiles.next(); if (file.getLastUpdated() < ifOlderThan) { updateFile(file, blob); } } else { folder.createFile(blob); } } function updateFile(file, blob) { const url = 'https://www.googleapis.com/upload/drive/v2/files/' + file.getId() + '?uploadType=media'; const params = { method: 'put', headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() }, payload: blob, muteHttpExceptions: true }; var response = UrlFetchApp.fetch(url, params); if (response.getResponseCode() < 200 || response.getResponseCode() > 299) { console.error('更新文件失败:' + file.getName() + ',响应码:' + response.getResponseCode()); throw 'Failed to update file named ' + file.getName(); } } function getPdfBlob(file) { var blob = file.getAs('application/pdf'); return blob; } function getNativeBlob(file) { const nativeMimeType = NATIVE_MIME_TYPES[file.getMimeType()]; const extension = NATIVE_EXTENSIONS[file.getMimeType()]; const url = 'https://www.googleapis.com/drive/v2/files/' + file.getId() + '/export?mimeType=' + nativeMimeType; const params = { method: 'get', headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() }, muteHttpExceptions: true }; const response = UrlFetchApp.fetch(url, params); if (response.getResponseCode() < 200 || response.getResponseCode() > 299) { console.error('导出文件失败:' + file.getName() + ',响应码:' + response.getResponseCode()); throw 'Failed to export file named ' + file.getName(); } const blob = response.getBlob(); blob.setName(file.getName() + extension); return blob; }
额外调试建议
- 查看执行日志:在脚本编辑器中点击「查看」→「日志」,检查触发器运行时的错误信息。
- 重新创建触发器:删除现有触发器,重新创建并完成授权流程,确保权限生效。
内容的提问来源于stack exchange,提问作者Kayley Harrison

