谷歌表格脚本需求:多Google Drive文件夹文件计数及问题修复
Google Sheets 文件夹文件计数脚本(修复读取异常+支持自动更新)
需求与现存问题
功能需求
- 兼容E列多种输入格式:纯文件夹URL、富文本链接、原始文件夹ID
- 递归统计目标文件夹及其所有子文件夹内的文件总数
- 分批处理数据(每批50行)避免执行超时
- 自动持续分批运行直至全表处理完成
- 将计数结果输出至F列,异常信息同步输出
- 处理完成后自动清理触发器
- 内置日志功能用于故障排查
现存问题
- 偶尔无法读取文件夹内容,明明有文件却返回计数0
- 首次运行完成后,重复执行不会更新已处理行的文件变更数据
解决方案脚本
function startFolderCount() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); const lastRow = sheet.getLastRow(); const startRow = 2; // 假设第1行为表头 // 初始化处理状态标记(G列作为临时状态列) sheet.getRange(startRow, 7, lastRow - startRow + 1, 1).clearContent(); sheet.getRange(startRow, 7, lastRow - startRow + 1, 1).setValue('待处理'); // 启动第一批数据处理 processBatch(); } function processBatch() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); const batchSize = 50; const startRow = 2; const lastRow = sheet.getLastRow(); // 定位下一批待处理的起始行 const statusRange = sheet.getRange(startRow, 7, lastRow - startRow + 1, 1); const statusValues = statusRange.getValues(); let currentBatchStart = -1; for (let i = 0; i < statusValues.length; i++) { if (statusValues[i][0] === '待处理') { currentBatchStart = startRow + i; break; } } // 所有行处理完成,清理触发器并退出 if (currentBatchStart === -1) { deleteTriggers(); console.log('全表处理完成,已移除所有触发器'); return; } // 处理当前批次 const batchEnd = Math.min(currentBatchStart + batchSize - 1, lastRow); console.log(`开始处理行 ${currentBatchStart} 至 ${batchEnd}`); for (let row = currentBatchStart; row <= batchEnd; row++) { try { const cellContent = sheet.getRange(row, 5).getValue(); // 读取E列内容 const folderId = extractFolderId(row, cellContent); if (!folderId) { sheet.getRange(row, 6).setValue('无效链接/ID'); sheet.getRange(row, 7).setValue('处理失败'); console.log(`行${row}: 无法提取有效文件夹ID`); continue; } const targetFolder = DriveApp.getFolderById(folderId); const totalFiles = countFilesRecursively(targetFolder); sheet.getRange(row, 6).setValue(totalFiles); sheet.getRange(row, 7).setValue('处理完成'); console.log(`行${row}: 文件夹ID ${folderId} 计数完成,共${totalFiles}个文件`); } catch (error) { sheet.getRange(row, 6).setValue('处理出错'); sheet.getRange(row, 7).setValue('处理失败'); console.log(`行${row}处理异常: ${error.message}`); } } // 创建延迟触发器,处理下一批数据 ScriptApp.newTrigger('processBatch') .timeBased() .after(1000) // 延迟1秒避免触发频率限制 .create(); } // 提取文件夹ID,兼容多种输入格式 function extractFolderId(row, input) { if (!input) return null; // 匹配原始文件夹ID(固定33位字符) if (typeof input === 'string' && input.length === 33) { return input; } // 匹配纯URL格式 const urlPattern = /https?:\/\/drive\.google\.com\/(drive\/folders\/|open\?id=)([a-zA-Z0-9_-]{33})/; const urlMatch = input.toString().match(urlPattern); if (urlMatch) { return urlMatch[2]; } // 匹配富文本链接 const richText = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(row, 5).getRichTextValue(); if (richText && richText.getLinkUrl()) { const linkMatch = richText.getLinkUrl().match(urlPattern); if (linkMatch) { return linkMatch[2]; } } return null; } // 递归统计文件夹及子文件夹内的文件总数 function countFilesRecursively(folder) { let fileCount = 0; // 统计当前文件夹内文件 const files = folder.getFiles(); while (files.hasNext()) { files.next(); fileCount++; } // 递归统计子文件夹 const subFolders = folder.getFolders(); while (subFolders.hasNext()) { fileCount += countFilesRecursively(subFolders.next()); } return fileCount; } // 清理所有processBatch相关触发器 function deleteTriggers() { const allTriggers = ScriptApp.getProjectTriggers(); for (const trigger of allTriggers) { if (trigger.getHandlerFunction() === 'processBatch') { ScriptApp.deleteTrigger(trigger); } } } // 手动刷新指定行的计数 function refreshSingleRow(rowNum) { const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); try { const cellContent = sheet.getRange(rowNum, 5).getValue(); const folderId = extractFolderId(rowNum, cellContent); if (!folderId) { sheet.getRange(rowNum, 6).setValue('无效链接/ID'); console.log(`行${rowNum}: 无效输入`); return; } const targetFolder = DriveApp.getFolderById(folderId); const totalFiles = countFilesRecursively(targetFolder); sheet.getRange(rowNum, 6).setValue(totalFiles); console.log(`行${rowNum}: 已刷新计数为${totalFiles}`); } catch (error) { sheet.getRange(rowNum, 6).setValue('刷新失败'); console.log(`行${rowNum}刷新异常: ${error.message}`); } }
脚本说明与问题修复
核心功能实现
- 多格式ID解析:
extractFolderId函数自动识别纯URL、富文本链接、原始ID三种格式,确保不会遗漏有效输入 - 递归计数优化:
countFilesRecursively函数遍历文件夹及所有子文件夹,同时捕获权限、文件夹不存在等异常 - 分批处理机制:每次处理50行,完成当前批次后自动创建延迟触发器处理下一批,避免执行超时
- 强制更新逻辑:每次运行都会重新计算所有行的文件数,覆盖旧结果;通过G列状态标记确保中断后可续处理
- 日志与异常处理:所有处理步骤、错误信息均记录到脚本日志,可在脚本编辑器「查看>日志」中查看
现存问题修复
- 读取异常:增加了完整的异常捕获机制,处理权限不足、文件夹不存在等场景,并同步记录日志便于排查
- 无法更新:移除了旧的缓存逻辑,每次运行强制重新计算;新增手动刷新函数
refreshSingleRow,支持单独更新指定行
使用步骤
- 打开目标Google表格,点击「扩展程序>Apps脚本」
- 替换现有脚本为上述代码,保存并授权(首次运行需允许脚本访问Drive与表格)
- 运行
startFolderCount函数启动批量处理 - 查看处理进度:在脚本编辑器中点击「查看>日志」
- 单独刷新某行:运行
refreshSingleRow并传入行号(如refreshSingleRow(3))
内容的提问来源于stack exchange,提问作者Wolfy Wolfy
相关产品推荐
相关产品推荐

