在自動化處理 Google 文件時,最常遇到的需求,其實很單純:指定一份文件,把它打開,然後寫點東西、抓點內容,再存檔收工。
DocumentApp.openById() 就是專做這件事的工具。只要你有文件的那串 ID,不論腳本是不是綁在文件裡,都能精準取得 Document 物件開始動工。這篇文章會先用簡單例子帶你跑一次開檔、寫入、存檔的流程,接著把常見的坑整理好:例如把整段 URL 當成 ID、權限不夠導致無法寫入、把 Drive 的 File 當成可編輯文件等等。
最後會補上幾個實務場景(批次報告、搜尋取代、樣式調整),再給你一份發佈前檢查清單,幫你避開小雷,寫出穩定可維護的程式。希望本篇文章能幫助到需要的您。
目錄
{tocify} $title={目錄}
為什麼要學 openById()?
在 Apps Script 自動化流程裡,你會常常需要 開啟特定的 Google 文件(Google Docs),再對內容進行巡檢、插入、格式化或導出。DocumentApp.openById(id) 正是這個場景的標準解法:只要有文件 ID,就能在程式中拿到 Document 物件,進一步操作段落、表格、清單、頁首頁尾與樣式。與容器綁定(container-bound)的 getActiveDocument() 相比,openById() 更適合批次處理、跨檔案作業與後台排程。
核心觀念速讀
方法定義:
DocumentApp.openById(id: string): Document。傳入文件的 ID(不是整段網址),回傳可編輯的 Document 物件。
何時用 openById()?
1. 你的程式不是從該文件裡「工具 > App Script」開出來(非 container-bound),或需要跨多個文件處理。
2. 你要在排程(time-based trigger)中批次巡檢或生成報告。
與 getActiveDocument() 差別:
後者只適合容器綁定腳本中,操作「當前文件」;跨檔、後台、自動化,用 openById() 更穩。
與 openByUrl() 差別:
兩者都能開啟指定文件;如果你只拿得到完整網址,用 openByUrl() 比較直接,但在儲存與傳遞參數時,ID 更精簡好管理。
授權範圍(Scopes):
視你對文件的操作而定,常見是 https://www.googleapis.com/auth/documents 或 .../documents.currentonly。第一次執行會跳授權對話框。
取得文件 ID 的正確方式
1. 在 Google 文件網址中,https://docs.google.com/document/d/<這一段就是ID>/edit。
2. 不要包含 /edit、查詢參數或錨點。只保留中間那段字串即可。
3. 長度通常為一串英數與底線的組合。
小技巧:若你手上是 URL,可先用 DocumentApp.openByUrl(url) 開檔,再 doc.getId() 存回系統設定或資料表,下次就用 ID。
逐步操作:第一次就成功
步驟 1:準備 Apps Script 專案
1. 到 [script.new] 建立新專案(或從任一 Google 文件的「擴充功能 > Apps Script」開啟)。
2. 把下面範例貼到 Code.gs。
function openDocByIdDemo() {
// TODO: 換成你的文件 ID
const DOC_ID = '1AbCDEFgHIjkLmNoPqRStuVwxyz_1234567890';
const doc = DocumentApp.openById(DOC_ID); // ① 開檔
const body = doc.getBody(); // ② 取得文件本體
body.appendParagraph('Hello from Apps Script'); // ③ 寫入一行文字
doc.saveAndClose(); // ④ 儲存並關閉
}
第一次執行會跳出授權,依指示同意即可。saveAndClose() 在腳本結束時也會自動觸發;顯式呼叫有助於確保批次流程穩定完成。
步驟 2:以參數傳入 ID(較適合批次)
function writeLineToDoc(docId, text) {
const doc = DocumentApp.openById(docId);
doc.getBody().appendParagraph(text);
doc.saveAndClose();
}
步驟 3:從表單或試算表批次處理(常見場景)
function batchComment() {
const sheet = SpreadsheetApp.getActiveSheet();
const rows = sheet.getDataRange().getValues();
// 假設 A 欄是 Doc ID,B 欄是要寫入的訊息
for (let i = 1; i < rows.length; i++) {
const [docId, message] = rows[i];
if (!docId || !message) continue;
try {
const doc = DocumentApp.openById(docId);
doc.getBody().appendParagraph(`[批次訊息] ${message}`);
doc.saveAndClose();
} catch (err) {
// 記錄錯誤,避免整批中斷
sheet.getRange(i + 1, 3).setValue(`Failed: ${err.message}`);
}
}
}
進階用法與常見需求
讀取內容、搜尋取代、調整樣式
function findAndReplace(docId, query, replacement) {
const body = DocumentApp.openById(docId).getBody();
body.replaceText(query, replacement); // 支援正則
}
function addHeading(docId, text) {
const p = DocumentApp.openById(docId).getBody().appendParagraph(text);
p.setHeading(DocumentApp.ParagraphHeading.HEADING2);
}
Apps Script 的 Document 類別提供豐富的段落、表格、清單、文字樣式 API;開檔之後就能直接用。
處理多檔案(報表匯整/巡檢)
function summarizeDocs(docIds) {
const result = [];
docIds.forEach(id => {
const doc = DocumentApp.openById(id);
const title = doc.getName();
const firstParagraph = doc.getBody().getParagraphs()[0]?.getText() || '';
result.push({ id, title, firstParagraph });
doc.saveAndClose();
});
Logger.log(result);
}
只讀 vs 可寫的權限議題
只要你的帳號對文件至少有讀取權,openById() 就能開檔;要寫入內容則需有編輯權。部署成外掛或為他人執行時,務必確認授權範圍與文件分享設定吻合。
DriveApp 與 DocumentApp 的分工
DriveApp 管「檔案殼與雲端硬碟操作」(搬家、複製、權限),不負責編輯文件內容;要改內容仍得回到 DocumentApp。兩者常一起用:先用 DriveApp 找到目標檔,再把 ID 丟進 openById()。
.docx 不是 Google 文件
openById() 只能打開 Google Docs 原生文件。若是上傳的 docx,需先轉成 Google Docs 格式才能用 DocumentApp 編輯;可透過進階服務 Drive API 轉檔。
常見問題與雷點
1. Exception: The document is inaccessible
常見原因:
ID 打錯、夾到 /edit 或其他多餘字元。
檔案在垃圾桶或已被刪除/移轉擁有者。
你沒有該檔案的存取權(權限被移除、非同網域)。
怎麼查:
直接把 ID 貼到 https://docs.google.com/document/d/<ID>/edit 測試是否能開。
檢查分享權限與是否在共用雲端硬碟(Shared Drive)。
檢視程式授權範圍是否包含 .../auth/documents。
2. 授權彈窗與 Scope 不符
第一次跑會跳授權,若你之後改了程式動作(例如新增寫入),需要重新同意更高權限。到「專案設定 > 以文字檔查看清單」確認自動偵測的範圍是否包含文件操作。
3. 把 URL 當 ID 用
典型寫法錯誤:
DocumentApp.openById('https://docs.google.com/document/d/xxx/edit') → 會錯。
正確:只丟中間那段 ID。
4. 把 DriveApp.getFileById() 當成內容編輯器
DriveApp.getFileById() 回傳的是 File,不是 Document;你無法藉此修改段落與樣式。請把 File.getId() 丟回 openById() 再操作內容。
5. 跨專案/外掛情境的權限錯配
外掛或 WebApp 以「執行者」或「存取者」身分跑,導致實際權限不同;請明確設定部署方式,並測試最低權限帳號情境。
6. .docx 直開失敗
Apps Script 的 Document 服務只吃 Google Docs 原生格式;上傳的 docx 必須先轉檔。
實務範例
範例 A:由 URL 自動萃取 ID 再開檔
function openByUrlSmart(url) {
// 從 URL 擷取 /d/<ID>/ 之間的 ID
const match = url.match(/\/document\/d\/([a-zA-Z0-9_-]+)/);
if (!match) throw new Error('無法從網址解析出文件 ID');
const id = match[1];
return DocumentApp.openById(id);
}
範例 B:批次產出報告文件(標頭+表格)
function writeWeeklyReport(docId, rows) {
const doc = DocumentApp.openById(docId);
const body = doc.getBody();
body.appendParagraph('週報').setHeading(DocumentApp.ParagraphHeading.HEADING1);
const table = body.appendTable();
table.appendTableRow().appendTableCell('項目').appendTableCell('狀態').appendTableCell('備註');
rows.forEach(r => {
const tr = table.appendTableRow();
r.forEach(cell => tr.appendTableCell(String(cell ?? '')));
});
doc.saveAndClose();
}
表格、段落等內容元素皆可透過 Document 類別操作。
範例 C:.docx 轉 Google Docs 後再用 openById() 編輯
需要在「服務」啟用 Advanced Drive Service。簡化示意如下:
function convertDocxAndOpen(fileId) {
// 1) 讀取 Drive 上的 .docx 檔案
const file = Drive.Files.get(fileId); // Advanced Drive
if (file.mimeType !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
throw new Error('不是 .docx 檔');
}
// 2) 轉成 Google Docs
const converted = Drive.Files.copy(
{ title: file.title.replace(/\.docx$/i, ''), mimeType: 'application/vnd.google-apps.document' },
fileId,
{ convert: true }
);
// 3) 用 openById() 開啟並寫入
const doc = DocumentApp.openById(converted.id);
doc.getBody().appendParagraph('已從 .docx 轉檔並新增一行文字');
doc.saveAndClose();
return converted.id;
}
背後原理與注意事項可參考轉檔最佳實務。
效能與穩定度建議(批次/排程友善)
合併寫入:
能一次 appendParagraph 就不要一行行寫迴圈;若必須多次操作,集中在同一次開檔期間完成,再 saveAndClose()。
錯誤續跑:
批次處理時,每個文件都包 try...catch,錯誤記錄到試算表或日誌,不要因單筆失敗拖垮整批。
權限前置檢查:
部署前,用最低權限帳號測一次,確定能開檔+寫入。
ID 管理:
把文件 ID 放在環境設定表或屬性服務(PropertiesService)中,保持可維護。
避免頻繁開關檔:
同一文件內的多步驟操作,只開一次,最後再關。
問題集
Q1:共用雲端硬碟(Shared Drive)的檔案能開嗎?
可以,只要腳本執行身分 對該檔案有權限。若透過服務帳戶或外掛,請確認授權設定。
Q2:我只想讀不想改,有沒有更小的 Scope?
可考慮以只讀的 .../documents.currentonly,但實際可用性仍取決於你呼叫的 API 動作。
Q3:我從別人給的分享連結開不起來?
多半是 ID 取錯 或 分享權限不含你的帳號。先用瀏覽器把 ID 拼成 /d/<ID>/edit 測試;若輸入正確仍不行,請對方檢查權限。
Q4:我需要更細緻的段落控制或批次格式化,DocumentApp 夠嗎?
若遇到極細節的版面與結構控制,考慮 Google Docs API(進階服務或直接呼叫 REST);它比 DocumentApp 更底層但也更繁瑣。
最常見錯誤訊息與對應處理
1. Exception: The document is inaccessible. Please try again later.
檢查:ID 是否正確/權限是否足夠/檔案是否為原生 Google Docs/是否在垃圾桶。
2. TypeError: Cannot read properties of undefined ...
多半是回傳的物件未取得(例如 getBody() 前檔案沒開到),逐步印出中間值確認。
3. 授權相關錯誤(Authorization required / insufficient permissions)
重新授權、確認部署身分(執行者 vs 使用者)、檢視 Scopes 是否包含文件操作。
