很多人第一次用 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。
