You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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("批次处理已停止");
}

使用说明

  1. 初始化:先运行initBatchProcess(),会自动创建状态表和文件夹表,并初始化根文件夹的处理状态。
  2. 自动分批次:脚本会自动设置触发器,每1分钟处理一批文件夹,直到所有文件夹遍历完成。
  3. 停止处理:如果需要中断,运行stopBatchProcess()即可删除所有待触发的任务。
  4. 调整批次大小:根据实际情况修改batchSize变量,比如文件夹数量特别多可以设为1000,若单批次仍超时则调小。

内容的提问来源于stack exchange,提问作者Jeffery Price

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 13:30:52