想把雲端硬碟整理好、檔案自動歸位,但又不想被一堆設定搞到頭痛?先學一招就夠用:DriveApp.getFolderById()。
它的重點很單純——用資料夾的「ID」精準鎖定目標,不被同名資料夾迷惑。這篇文章會用最少的前置知識,從取得 ID、基本操作(列出檔案、建立子資料夾、搬移檔案)一路講到錯誤處理與權限眉角;還會列出常見的踩雷案例,像迭代器用法、共享雲端硬碟的權限差異、以及速率限制下的重試策略。
看完就能快速上手,把日常固定流程交給腳本跑,省時、少錯、也更好維護。
希望本篇文章可以幫助到需要的您。
目錄
{tocify} $title={目錄}
基礎概念:DriveApp 與 getFolderById()
DriveApp 是 Google Apps Script 內建的服務,用來操作使用者有權限的雲端硬碟內容(建立、搜尋、搬移、刪除…)。
getFolderById(id) 會回傳對應的 Folder 物件,之後你就能呼叫像 getFiles(), createFile(), createFolder() 等方法。
與 getFoldersByName(name) 比起來,透過 ID 取用是「唯一且穩定」的,不會因為同名資料夾而抓錯。
先搞清楚「資料夾 ID」從哪來(含 URL 判讀)
1. 從網址列取得
在雲端硬碟開啟某資料夾,網址大多長這樣:
https://drive.google.com/drive/folders/1A2b3CdE4FgHiJkLmNOPqRSTuvwxYZ
folders/ 後面那串字母數字就是 資料夾 ID(不含其他查詢參數)。
2. 從「共用連結」取得
有時候別人丟你這種:
https://drive.google.com/drive/u/0/folders/1A2b3CdE4FgHiJkLmNOPqRSTuvwxYZ?usp=drive_link
? 之前一樣是 ID;只要擷取 folders/ 後到下一個分隔符號為止。
3. 從 Drive API/進階 Drive 服務取得
若你用過進階 Drive 服務(或 Drive API)搜尋資料夾,回傳的 id 欄位就是同一個東西。本文聚焦 Apps Script 內建 DriveApp,不額外依賴進階服務。
迷你避雷:不要把「檔案 ID」拿去給 getFolderById()。檔案與資料夾的 ID 長得都像亂數,但指向不同型別物件。傳錯會拋錯誤。
權限與授權:哪種帳號/雲端空間會踩坑?
你必須對該資料夾有至少「檢視」權限,否則會噴權限錯誤。
若那個資料夾在 共享雲端硬碟(Shared drives) 裡,執行腳本的帳號也要有對應雲端硬碟的存取權。
Web App/執行觸發器 情境:看「以誰的身份執行」。
以「部署者」身份執行 → 用部署者的權限去存取資料夾。
以「存取者」身份執行 → 用使用者自己的權限;對方沒權限也會失敗。
首次執行會跳出授權,確保你接受 查看及管理 Google 雲端硬碟中的檔案 這類權限範圍。
最小可用範例(Hello Folder)
場景:列出指定資料夾下所有檔案名稱。
function listFilesInFolder() {
var folderId = '替換成你的資料夾ID';
var folder = DriveApp.getFolderById(folderId);
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
Logger.log(file.getName());
}
}
如果你看到 Exception: Cannot retrieve the next object: iterator has reached the end.,多半是 while (files.hasNext()) 用錯或誤用 next() 方式;或你在 hasNext() 之後又多呼叫一次 next()。
進階實務範例
1. 依日期自動建立月份資料夾(若不存在就建立)
function ensureMonthlyFolder(parentFolderId) {
var parent = DriveApp.getFolderById(parentFolderId);
var name = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM');
var folders = parent.getFoldersByName(name);
if (folders.hasNext()) return folders.next();
return parent.createFolder(name);
}
2. 搬移報表檔案到指定資料夾
function moveReportToFolder(fileId, destFolderId) {
var file = DriveApp.getFileById(fileId);
var dest = DriveApp.getFolderById(destFolderId);
// 新增目的地父層
file.moveTo(dest);
// 備註:在 My Drive moveTo 會「重新指定父層」;
// 在 Shared drives,父層關係較單一,moveTo 行為一致但受權限控管影響。
}
3. 列出子資料夾與檔案(含層級縮排)
function printTree(folderId, indent) {
indent = indent || '';
var folder = DriveApp.getFolderById(folderId);
Logger.log(indent + '[Folder] ' + folder.getName());
var subFolders = folder.getFolders();
while (subFolders.hasNext()) {
var sub = subFolders.next();
printTree(sub.getId(), indent + ' ');
}
var files = folder.getFiles();
while (files.hasNext()) {
var f = files.next();
Logger.log(indent + '- ' + f.getName());
}
}
4. 依副檔名分類歸檔
function archiveByExtension(srcFolderId, destRootFolderId) {
var src = DriveApp.getFolderById(srcFolderId);
var files = src.getFiles();
while (files.hasNext()) {
var f = files.next();
var name = f.getName();
var ext = name.includes('.') ? name.split('.').pop().toLowerCase() : 'noext';
var dest = ensureChildFolder(destRootFolderId, ext);
f.moveTo(dest);
}
}
function ensureChildFolder(parentId, name) {
var parent = DriveApp.getFolderById(parentId);
var it = parent.getFoldersByName(name);
return it.hasNext() ? it.next() : parent.createFolder(name);
}
5. 導入安全檢查:確認 ID 指向的確是資料夾
function safeGetFolderById(id) {
// 低成本檢查:嘗試 getFolderById 並在例外中提供更明確訊息
try {
var folder = DriveApp.getFolderById(id);
// 額外驗證:用個無害操作確認物件活著
folder.getName();
return folder;
} catch (e) {
throw new Error('取得資料夾失敗,請檢查 ID 是否為資料夾而非檔案、以及權限:' + e.message);
}
}
穩健性:錯誤處理、重試、速率限制(Quota)
1. try/catch 與可讀性錯誤訊息
function robustList(folderId) {
try {
var folder = DriveApp.getFolderById(folderId);
var files = folder.getFiles();
var names = [];
while (files.hasNext()) names.push(files.next().getName());
Logger.log('檔案數:' + names.length);
} catch (err) {
Logger.log('[錯誤] ' + err.message);
// 可依訊息字串判斷權限/不存在/參數
}
}
2. API 速率限制與短暫錯誤(429/5xx)重試
Apps Script 對 DriveApp 也會有節流。若批次操作量大,請:
批次處理時加入 Utilities.sleep(100~500) 微間隔。
實作「指數退避」重試機制(遇暫時性錯誤再試 2~3 次)。
function withRetry(fn, times) {
times = times || 3;
var delay = 200;
for (var i=0; i<times; i++) {
try { return fn(); }
catch (e) {
if (i === times - 1) throw e;
Utilities.sleep(delay);
delay *= 2;
}
}
}
防呆封裝:安全取得資料夾的公用函式(可直接搬用)
/**
* 以資料夾 ID 安全取得 Folder 物件,並附帶自動診斷。
* - 自動檢查空字串/空白
* - 於例外訊息中提示常見成因(ID 用錯、權限不足、Shared drives)
*/
function getFolderOrThrow(id) {
if (!id || typeof id !== 'string' || !id.trim()) {
throw new Error('資料夾 ID 不可為空');
}
// 粗略校驗:Drive ID 常見為 20~80 的字母數字與特殊字元(非嚴格)
if (!/^[A-Za-z0-9_\-]{10,}$/.test(id)) {
Logger.log('提醒:這個看起來不像標準的 Drive ID,請再確認來源是否為 folders/... 後的那串。');
}
try {
var folder = DriveApp.getFolderById(id);
// 讀個屬性以確保物件可存取
folder.getName();
return folder;
} catch (e) {
var hint = '取得資料夾失敗。常見原因:\n' +
'1) 這不是資料夾 ID(可能是檔案 ID)。\n' +
'2) 你沒有該資料夾權限(請檢查共用設定/換帳號)。\n' +
'3) 這是共享雲端硬碟,腳本執行者沒有存取權。\n' +
'4) 資料夾被移到垃圾桶或已刪除。\n';
throw new Error(hint + '原始訊息:' + e.message);
}
}
常見錯誤與雷點
1. Exception: Invalid argument: id
成因:
把 檔案 ID 或 其他亂碼 傳給 getFolderById()。
ID 前後多了空白或異常字元。
排查:
重新從 https://drive.google.com/drive/folders/... 取得。
先 trim();必要時以正規式粗檢 /^[A-Za-z0-9_\-]{10,}$/。
避雷:
在接收外部輸入時,一律先跑 getFolderOrThrow(id) 防呆。
2. Exception: Access denied: DriveApp
成因:
沒有目標資料夾的存取權。
Web App 以使用者身份執行,而該使用者對目標資料夾無權限。
排查:
用同一個帳號手動打開連結確認能否看到。
若是共享雲端硬碟,確認「成員」權限。
避雷:
採用以部署者身份執行(視情境與資安政策),並最小化對象範圍。
3. 找不到(Not found)
成因:
ID 打錯或資料夾被刪/進垃圾桶。
排查:
讓擁有者重新提供新的有效 ID。
在 Drive 網頁搜 ID(搜尋列貼上)確認狀態。
4. 共享雲端硬碟權限行為與 My Drive 不同
症狀:
moveTo() 行為與想像不同,或遇到權限限制。
避雷:
在 Shared drives 中,檔案只有一個父層;你需要對來源與目的地都擁有足夠權限。
5. 迭代器使用錯誤(hasNext() / next())
成因:
while (files.hasNext()) 之外又多呼叫 next(),導致迭代越界。
避雷:
嚴格遵守:每次迴圈只呼叫一次 next(),並搭配 hasNext() 判斷。
6. 大量操作被節流(Quota/Ratelimit)
症狀:
間歇性錯誤、429 或 5xx。
避雷:
批次處理加上 sleep 與指數退避重試;分批執行;必要時排程在低峰時段。
7. 以為「同名唯一」= 安全
業務長期運作下,同名資料夾很常出現。禁止以名稱當主鍵;用 ID。
安全與治理:最小權限、稽核、記錄
最小權限原則:只給腳本需要的資料夾與動作權限。
動作記錄:把成功/失敗、搬移前後 ID、檔案名稱記錄到試算表或記錄表,日後稽核好查。
錯誤告警:關鍵流程失敗時,寄送 Gmail 通知或在 Chat Webhook(如 Slack、Chat)發訊。
問題集
Q1:為什麼不用 getFoldersByName()?
因為名稱不保證唯一。當你移交專案、換人維運、或使用者誤建同名資料夾時,程式會抓錯對象。getFolderById() 才是長期穩定解。
Q2:My Drive 與 Shared drives 差在哪?
Shared drives 下權限更嚴格、父層關係單一。確保你在 來源與目的地 都有權限,並留意組織層級的分享政策。
Q3:如何從網址快速擷取 ID?
拿到 https://drive.google.com/drive/folders/<ID>?xxx,把 <ID> 抽出。你也可在 UI 上右鍵「取得連結」並複製。
Q4:能否先檢查 ID 是不是資料夾?
內建沒有「型別檢查器」,但你可以呼叫 getFolderById() 包 try/catch,或搭配進階 Drive 服務檢查 mimeType 是否為 application/vnd.google-apps.folder(需要啟用並調整授權)。
Q5:部署成 Web App 會不會權限踩雷?
看你設定「以誰的身份執行」。若收件人沒有對資料夾的權限,選擇「以執行者」可避免權限不足,但伴隨資安考量,請配合公司規範。
範例片段總表
A. 安全取得資料夾(含提示)
function getFolderOrThrow(id) {
if (!id || typeof id !== 'string' || !id.trim()) throw new Error('資料夾 ID 不可為空');
if (!/^[A-Za-z0-9_\-]{10,}$/.test(id)) Logger.log('提醒:這看起來不像標準 Drive ID,請再確認。');
try {
var folder = DriveApp.getFolderById(id);
folder.getName();
return folder;
} catch (e) {
var hint = '取得資料夾失敗,可能原因:不是資料夾 ID/權限不足/已刪除或在共享雲端硬碟中無權限。\n';
throw new Error(hint + '原始訊息:' + e.message);
}
}
B. 列出檔名(正確使用迭代器)
function listNames(folderId) {
var folder = getFolderOrThrow(folderId);
var it = folder.getFiles();
while (it.hasNext()) {
Logger.log(it.next().getName());
}
}
C. 依月份建立歸檔資料夾並移動檔案
function archiveToMonthly(fileId, archiveRootId) {
var file = DriveApp.getFileById(fileId);
var root = getFolderOrThrow(archiveRootId);
var month = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM');
var dest = (function(){
var it = root.getFoldersByName(month);
return it.hasNext() ? it.next() : root.createFolder(month);
})();
file.moveTo(dest);
}
D. 大量處理(含簡單退避)
function batchMove(fileIds, destId) {
var dest = getFolderOrThrow(destId);
var delay = 100;
for (var i=0; i<fileIds.length; i++) {
withRetry(function(){
var f = DriveApp.getFileById(fileIds[i]);
f.moveTo(dest);
}, 3);
Utilities.sleep(delay);
if (delay < 800) delay += 50;
}
}
踩坑注意
getFolderById() 的核心只有一個參數,但真正影響穩定度的是 ID 來源、權限模型、與錯誤處理。
你若把:
ID 來源固定且可驗證、
權限與執行身分想清楚、
例外處理與重試補上,
基本上就能放心把流程交給機器跑。之後要擴充(像自動歸檔、報表輸出、收集上傳)也會更好維護。
快速摘要
用途:以唯一 ID 精準定位雲端硬碟資料夾,避免同名混淆。
關鍵步驟:從 folders/<ID> 取 ID → DriveApp.getFolderById() 取得 Folder → 進行檔案/資料夾操作。
大坑:ID 用錯(檔案當資料夾)、權限不足(特別是 Shared drives)、迭代器誤用、批次操作無節流。
建議:封裝 getFolderOrThrow()、加入重試與記錄、釐清 Web App/觸發器的執行身分。
