Google 雲端硬碟:透過ID取得資料夾 getFolderById()

 


想把雲端硬碟整理好、檔案自動歸位,但又不想被一堆設定搞到頭痛?先學一招就夠用: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/觸發器的執行身分。


張貼留言 (0)
較新的 較舊