Google 雲端硬碟:確認資料夾或檔案是否存在 hasNext()

 



很多人第一次用 DriveApp,都直覺想「把檔案變陣列一次處理完」,結果不是超時就是記憶體爆表。其實 Apps Script 已經幫你設計好更安全的路線:用 Iterator 慢慢走,hasNext() 先看前方有沒有東西,再用 next() 拿資料。

這篇文章會先用白話把 Iterator 的概念講清楚,再帶你做三件事:一是把迴圈骨架寫到不會出錯;二是用搜尋條件與資料夾範圍,把掃描成本降到最低;三是學會 continuation token,把長任務切片完成。最後附上避雷清單與可直接複用的程式片段,讓你之後面對上千上萬個檔案,也能照樣穩穩跑完。希望本篇文章可以幫助到需要的您。


目錄

{tocify} $title={目錄} 


為什麼你需要懂 hasNext()

在 Apps Script 裡,Drive 的檔案/資料夾查詢,常回傳「Iterator(迭代器)」而不是一次性陣列。典型像 DriveApp.getFiles()、getFolders()、searchFiles(...) 等,都會給你 FileIterator 或 FolderIterator。hasNext() 的任務,就是告訴你接下來還有沒有下一個項目,搭配 next() 才能安全地逐一處理,不爆錯、不漏資料,還能接上續傳(continuation token)處理大批量。


觀念先釐清:Iterator 的三兄弟

1.    hasNext():回傳 true/false,表示呼叫 next() 是否還拿得到資料。

2.    next():真的取出下一個項目(File 或 Folder),若沒有就丟例外。

3.    getContinuationToken() / DriveApp.continueFileIterator(token):把目前迭代進度存成 token,下次可從斷點繼續(token 一般有效期約一週)。用於大批量、超時風險高的工作流程。

小重點:Apps Script 的 Iterator 最適配的寫法是 while (it.hasNext()) { ... it.next() ... },比 for 好讀、也最直觀。


入門操作:三個最常見的 hasNext() 範例

1.    列出雲端硬碟所有檔案名稱

function listAllFiles() {
  const files = DriveApp.getFiles(); // FileIterator
  while (files.hasNext()) {
    const file = files.next(); // File
    Logger.log(file.getName());
  }
}


說明:getFiles() 會回傳使用者 Drive 的全部檔案集合,逐一用 hasNext() + next() 取出。


2.    只處理特定資料夾底下的檔案

function listFilesInFolder(folderId) {
  const folder = DriveApp.getFolderById(folderId);
  const files = folder.getFiles(); // FileIterator
  while (files.hasNext()) {
    const file = files.next();
    Logger.log(`${folder.getName()} / ${file.getName()}`);
  }
}


說明:getFolderById() 搭配 getFiles() 篩到特定範圍,避免掃整個雲端的成本。


3.    依條件搜尋(名稱包含關鍵字、或指定 MIME 類型)

function searchDocs() {
  // 1) MIME 類型篩 Google 文件
  const docs = DriveApp.getFilesByType(MimeType.GOOGLE_DOCS);
  while (docs.hasNext()) {
    Logger.log(docs.next().getName());
  }

  // 2) 自訂查詢語法(Drive v2 風格)
  const query = 'title contains "報表" and trashed=false';
  const files = DriveApp.searchFiles(query);
  while (files.hasNext()) {
    Logger.log(files.next().getName());
  }
}


說明:getFilesByType(MimeType.GOOGLE_DOCS) 與 searchFiles(...) 都會回 FileIterator,仍是 hasNext() + next() 的節奏;注意 Apps Script Drive 服務採 Drive API v2 查詢語法(與 v3 略有差異)。


進階:長任務的續跑設計(Continuation Token)

批次很大時,Apps Script 常遇到 6 分鐘(一般觸發器)或 30 分鐘(部分觸發類型)的時限。善用續傳可把長任務切段執行:

function processLargeBatch() {
  const prop = PropertiesService.getScriptProperties();
  const token = prop.getProperty('FILES_TOKEN');
  const iterator = token
      ? DriveApp.continueFileIterator(token)
      : DriveApp.getFiles();

  let count = 0;
  const MAX_PER_RUN = 500; // 每次處理上限,視情況調整

  while (iterator.hasNext() && count < MAX_PER_RUN) {
    const file = iterator.next();
    // TODO: 對 file 做你的處理
    count++;
  }

  if (iterator.hasNext()) {
    const nextToken = iterator.getContinuationToken();
    prop.setProperty('FILES_TOKEN', nextToken);
    // 可搭配時間觸發器排下一輪
  } else {
    prop.deleteProperty('FILES_TOKEN'); // 結束
  }
}


核心要點:

getContinuationToken() 取斷點;下次用 DriveApp.continueFileIterator(token) 延續。

Token 通常 有效約一週,務必設計在此期限內輪完。


hasNext() 常見誤用與例外處理

1.    沒先判斷 hasNext() 就 next()

會直接丟例外,導致整批中斷——務必堅持 while 寫法。

2.    用錯查詢/篩選導致永遠 false

例如 getFilesByName('xxx') 沒精準匹配、或 searchFiles 的 v2 查詢語法寫錯,Iterator 一開始就空集合。先用小集合驗證查詢語句。

3.    誤以為 for...of 可以吃 Iterator

Apps Script 的 Drive Iterator 不是標準 JS 迭代協定物件,請用 while (hasNext())。

4.    跨共享雲端硬碟(Shared Drives)需求卻只用 DriveApp

DriveApp 好用但功能有限,遇到權限/欄位或進階查詢需求,請評估**進階 Drive 服務(Advanced Drive Service)**或直接用 Drive API。

5.    把 iterator 強行展開成陣列一次吃光

容易撞時限與記憶體限制;應逐筆處理,或分段+ token 續跑。

6.    token 長期不清理

流程完成要清掉 FILES_TOKEN,避免下次誤從中段開始。

7.    名稱重複引發誤判

getFilesByName(name) 可能拿到多個同名檔,務必逐筆檢查父資料夾或其他屬性再決策。


實務範例

A. 批次搬運:把特定關鍵字的檔案移到指定資料夾

function moveReportsTo(folderId) {
  const dst = DriveApp.getFolderById(folderId);
  const q = 'title contains "報表" and trashed=false';
  const it = DriveApp.searchFiles(q);

  while (it.hasNext()) {
    const f = it.next();
    f.moveTo(dst);
  }
}


說明:searchFiles 查到的是 FileIterator,用 hasNext() 安全跑完。


B. 型別篩選:僅處理 PNG 檔的檔名與大小

function logPngSizes() {
  const pngs = DriveApp.getFilesByType('image/png');
  while (pngs.hasNext()) {
    const p = pngs.next();
    Logger.log(`${p.getName()} : ${p.getSize()}`);
  }
}


說明:官方文件提供 MimeType 列舉與字串兩種寫法,都能搭 hasNext()。


C. 分段大批次:建立大量捷徑並可中斷續跑

function createShortcutsWithToken() {
  const prop = PropertiesService.getScriptProperties();
  const token = prop.getProperty('F_TOKEN');
  const it = token ? DriveApp.continueFileIterator(token) : DriveApp.getFiles();

  let i = 0, LIMIT = 300;
  while (it.hasNext() && i < LIMIT) {
    const f = it.next();
    // 假設要幫每個檔案在根目錄建一個捷徑
    DriveApp.createShortcut(f.getId());
    i++;
  }

  if (it.hasNext()) {
    prop.setProperty('F_TOKEN', it.getContinuationToken());
  } else {
    prop.deleteProperty('F_TOKEN');
  }
}


說明:示範 continuation token 的標準輪巡模板。


效能與穩定性實務招

1.    縮小範圍先:能用 getFolderById(...).getFiles() 就別掃整個雲端。

2.    優先條件搜尋:用 searchFiles 先濾,再逐筆處理。

3.    分段+續跑:大批次務必加入處理上限與 token。

4.    日誌與重試:對偶發 429/5xx 可加上簡單 backoff;完成品請紀錄處理進度(如最後一個檔 ID)。

5.    權限先行:若涉共享雲端硬碟或更細權限控制,評估改用 Advanced Drive(或 UrlFetch 打 Drive API)。


常見錯誤與雷點

1.    TypeError: Cannot call method "next" of null

        多半是你的變數不是 Iterator(例如函式沒回傳、或回傳 null)。檢查回傳型別,確保用 DriveApp.getFiles() 等 API。

2.    Exception: No more items in iterator

        在沒有 hasNext() 的情況下硬 next()。把 for 全部改為 while (it.hasNext())。

3.    searchFiles 拿不到資料

        多為查詢語法(v2)寫錯:把 name 寫成 title?是否 trashed=false?先用小樣本測試查詢。

4.    跨資料夾搜尋不到

        別忘了在條件加入 '<folderId>' in parents,或先取得該資料夾再 getFiles()。

5.    效能很慢

        大範圍掃描 + 巨量回傳=慢且易超時。請用條件搜尋、切批處理+ continuation token。

6.    同名檔太多導致誤搬

        getFilesByName 可能回多個檔案,請再比對 parents 或 MIME 類型再動作。

7.    想直接用 for...of

        Apps Script 的 Drive Iterator 不是原生 JS iterator;請用 while (hasNext())。


範例模組:把 Iterator 包成產生器(更好測、更可複用)

雖然 Drive Iterator 不是原生 JS iterator,我們可以「包一層」讓商業邏輯用更乾淨的 for...of:

/**
 * 將 Apps Script 的 FileIterator 包裝成可用 for...of 的迭代器
 */
function* asIterable(fileIterator) {
  while (fileIterator.hasNext()) {
    yield fileIterator.next();
  }
}

function processDocsIterable() {
  const docs = DriveApp.getFilesByType(MimeType.GOOGLE_DOCS);
  for (const doc of asIterable(docs)) {
    // 商業邏輯在這裡
    Logger.log(doc.getName());
  }
}


這種包裝法仍然是底層用 hasNext() 控制流程,保留 Apps Script Iterator 的語義,又讓上層程式更好讀。


測試與除錯建議

1.    先用小範圍資料夾測試(建立一個 sandbox folder),確認查詢與流程正確再放大。

2.    記錄處理數量與最後進度(例如每 100 筆 Log 一次)。

3.    對「空集合」做防禦:Iterator 可能為空,程式不該報錯,而是優雅地結束。

4.    異常告警:用 MailApp.sendEmail 或 Chat Webhook 將錯誤通知自己(量大任務必備)。


問題集

Q1:為什麼我一直拿不到資料?

A:九成是查詢語法或範圍問題(例如掃整個雲端其實很慢),先縮到單一資料夾測試;searchFiles 請確保用 v2 欄位名(title 不是 name)。

Q2:要處理 10 萬個檔案會超時嗎?

A:會。請設計分批上限(如每次 500~2000 筆),加上 continuation token,下個觸發器再續。

Q3:共享雲端硬碟的特殊需求呢?

A:DriveApp 有侷限,建議改走 Advanced Drive / Drive API。


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