Google Apps Script教程:收集文件夹信息时绕过执行超时
解决Google Apps Script遍历大量文件夹超时问题
一、大幅加速代码的优化方案
原脚本的核心性能瓶颈是频繁调用sheet.appendRow()——每遍历一个文件夹就发起一次Sheets API请求,当文件夹数量上千时,耗时会急剧增加。以下是优化后的代码,通过批量写入数据将性能提升数倍甚至数十倍:
优化后代码
var SS = SpreadsheetApp.getActiveSpreadsheet(); function listFilesAndFolders(){ var folderId = 'Insert Root Folder to Search'; getFolderTree(folderId, true); }; // 初始化表格并递归收集数据 function getFolderTree(folderId, listAll) { try { var parentFolder = DriveApp.getFolderById(folderId); var sheet = SS.getSheetByName("Folders"); sheet.clear(); // 先把所有数据存入数组,最后一次性写入 var results = [["URL", "Full Path"]]; collectFolderData(parentFolder.getName(), parentFolder, results); // 批量写入数据,替代多次appendRow if (results.length > 1) { sheet.getRange(1, 1, results.length, results[0].length).setValues(results); } } catch (e) { Logger.log(e.toString()); } }; // 仅负责递归收集文件夹数据,不执行表格写入操作 function collectFolderData(parentPath, parentFolder, resultsArray) { var childFolders = parentFolder.getFolders(); while (childFolders.hasNext()) { var childFolder = childFolders.next(); var currentPath = parentPath + "/" + childFolder.getName(); resultsArray.push([ childFolder.getUrl(), currentPath ]); // 递归处理子文件夹 collectFolderData(currentPath, childFolder, resultsArray); } };
优化说明
- 批量写入:将所有文件夹数据先存入数组,最后用
setValues()一次性写入表格,API请求次数从N次(N为文件夹数量)降到1次,这是最核心的性能提升点。 - 减少重复操作:提前缓存Spreadsheet实例,避免重复调用
SpreadsheetApp.getActiveSpreadsheet()。 - 职责分离:递归函数只做数据收集,不处理表格操作,简化逻辑的同时减少递归中的不必要开销。
二、绕过30分钟执行超时的分批次处理方案
如果文件夹数量极大(比如数万级),即使优化后仍会超出30分钟执行上限,可采用脚本触发器+状态记录的方式拆分任务,分批次执行:
分批次处理代码
var SS = SpreadsheetApp.getActiveSpreadsheet(); var STATUS_SHEET = "状态"; var FOLDERS_SHEET = "Folders"; // 初始化批次处理流程 function initBatchProcess() { var rootFolderId = 'Insert Root Folder to Search'; var rootFolder = DriveApp.getFolderById(rootFolderId); // 创建或清空状态表,记录处理进度 var statusSheet = SS.getSheetByName(STATUS_SHEET) || SS.insertSheet(STATUS_SHEET); statusSheet.clear(); statusSheet.appendRow(["当前文件夹ID", "当前路径前缀", "处理状态"]); statusSheet.appendRow([rootFolderId, rootFolder.getName(), "未完成"]); // 创建或清空文件夹表 var foldersSheet = SS.getSheetByName(FOLDERS_SHEET) || SS.insertSheet(FOLDERS_SHEET); foldersSheet.clear(); foldersSheet.appendRow(["URL", "Full Path"]); // 启动第一批处理 processBatch(); } // 处理单批次文件夹 function processBatch() { var statusSheet = SS.getSheetByName(STATUS_SHEET); var lastStatusRow = statusSheet.getLastRow(); var statusData = statusSheet.getRange(lastStatusRow, 1, 1, 3).getValues()[0]; var currentFolderId = statusData[0]; var currentPathPrefix = statusData[1]; var processStatus = statusData[2]; if (processStatus === "已完成") { Logger.log("所有文件夹处理完成"); return; } var currentFolder = DriveApp.getFolderById(currentFolderId); var childFolders = currentFolder.getFolders(); var batchResults = []; var batchSize = 500; // 每批处理500个文件夹,可根据实际情况调整 var processedCount = 0; var nextFolderId = null; // 处理当前批次的文件夹 while (childFolders.hasNext() && processedCount < batchSize) { var childFolder = childFolders.next(); var fullPath = currentPathPrefix + "/" + childFolder.getName(); batchResults.push([childFolder.getUrl(), fullPath]); // 记录下一个待处理的文件夹ID(如果还有剩余) if (childFolders.hasNext()) { nextFolderId = childFolder.getId(); } processedCount++; } // 写入当前批次数据到文件夹表 var foldersSheet = SS.getSheetByName(FOLDERS_SHEET); var startRow = foldersSheet.getLastRow() + 1; if (batchResults.length > 0) { foldersSheet.getRange(startRow, 1, batchResults.length, 2).setValues(batchResults); } // 更新状态表 statusSheet.getRange(lastStatusRow, 3).setValue("已完成"); if (nextFolderId) { var nextFolder = DriveApp.getFolderById(nextFolderId); var nextPath = currentPathPrefix + "/" + nextFolder.getName(); statusSheet.appendRow([nextFolderId, nextPath, "未完成"]); } // 设置1分钟后自动触发下一批处理(可调整间隔) ScriptApp.newTrigger("processBatch") .timeBased() .after(60000) .create(); } // 手动停止批次处理 function stopBatchProcess() { var triggers = ScriptApp.getProjectTriggers(); for (var i = 0; i < triggers.length; i++) { if (triggers[i].getHandlerFunction() === "processBatch") { ScriptApp.deleteTrigger(triggers[i]); } } var statusSheet = SS.getSheetByName(STATUS_SHEET); statusSheet.getRange(statusSheet.getLastRow(), 3).setValue("已停止"); Logger.log("批次处理已停止"); }
使用说明
- 初始化:先运行
initBatchProcess(),会自动创建状态表和文件夹表,并初始化根文件夹的处理状态。 - 自动分批次:脚本会自动设置触发器,每1分钟处理一批文件夹,直到所有文件夹遍历完成。
- 停止处理:如果需要中断,运行
stopBatchProcess()即可删除所有待触发的任务。 - 调整批次大小:根据实际情况修改
batchSize变量,比如文件夹数量特别多可以设为1000,若单批次仍超时则调小。
内容的提问来源于stack exchange,提问作者Jeffery Price
相关产品推荐
相关产品推荐

