Google 文件:透過文件ID開啟文件 openById()

 


在自動化處理 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 是否包含文件操作。


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