1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GASで共有ドライブのファイルを判定するなら、共有範囲ではなく置き場所を見る

1
Posted at

社内向けの小さな登録フォームに、「貼られた Google ドライブのリンクが、決められた共有ドライブの中にあるか」をチェックする機能を付けました。サーバーから GAS の Web アプリに問い合わせ、置き場所が正しいかを返してもらう構成です。

最初は「共有範囲を見れば分かる」と考えていたのですが、共有ドライブのファイルではそれが通用しませんでした。ハマった点と、最終的に落ち着いた判定方法をまとめます。

結論

  • 共有ドライブのファイルは getSharingAccess() が PRIVATE、getOwner() が null を返すことがある。共有範囲・オーナーでは「社内の人が開けるか」を判定できない
  • 判定の問いを「決められた共有ドライブの中にあるか」に取り替え、getParents() で最上位までさかのぼって ID を比べる
  • 「ファイルが開けない」を「ドライブの外」と即断しない。ドライブ本体が開けるかを再確認して「外」と「不調」を分ける

ハマった点:共有範囲とオーナーで判定できない

最初に書いた判定はこれです。

const file = DriveApp.getFileById(fileId);
const access = String(file.getSharingAccess()); // ANYONE / DOMAIN / PRIVATE など
const owner = file.getOwner();
const email = owner ? owner.getEmail() : '';

共有範囲が DOMAIN か、オーナーが自社ドメインなら OK、という考えでした。マイドライブのファイルならこれで大体判定できます。

ところが、社員全員がメンバーになっている共有ドライブのファイルを調べると、結果はこうでした。

getSharingAccess() → PRIVATE
getOwner()         → null
  • オーナーが null: Drive API のファイルリソースの説明で、owners は「共有ドライブ内の項目では設定されない」とされています。共有ドライブのファイルは組織の所有なので、個人のオーナーがいません
  • PRIVATE: メンバーが開けるのは「共有ドライブのメンバーとしての権限」によるもので、ファイルの一般的なアクセス設定(リンクを知っている人・組織全体)が広いからではありません。getSharingAccess() は後者しか見ないため、メンバー全員が開けても PRIVATE になります(ここは私の理解です)

このままだと、正しく共有ドライブに置いた人ほど警告を受けることになります。

置き場所で判定する

公式ドキュメントにある共有ドライブの性質を2つ使います。

  • 共有ドライブの ID は、そのドライブの最上位フォルダの ID と同じ(https://drive.google.com/drive/folders/0A... の末尾がそのまま使える)
  • 共有ドライブ内のファイルは、親をちょうど1つだけ持つ

親を1つずつたどり、ドライブの ID に当たれば「中」、最上位で行き止まれば「外」です。

const MAX_DEPTH = 20;

/**
 * 自身か祖先の ID が driveId と一致 → true
 * 最上位まで行って一致しない       → false
 * MAX_DEPTH までに最上位に着かない → null(判定できない)
 */
function isInDrive(item, driveId) {
  let cur = item;
  for (let depth = 0; depth < MAX_DEPTH; depth++) {
    if (cur.getId() === driveId) return true;
    const parents = cur.getParents();
    if (!parents.hasNext()) return false;
    cur = parents.next();
  }
  return null;
}

ポイントは3つです。

  • 共有ドライブの最上位フォルダそのもののリンクが貼られることもあるので、最初に自身の ID も比べる
  • getParents() が空になったら最上位に着いた合図
  • 戻り値を true/false にせず、null(判定できない)を分ける

なお、実行アカウントがその共有ドライブのメンバーでないと途中で開けなくなります。Web アプリは「自分として実行」にし、実行アカウントをメンバーに入れておきます。

開けないファイルは「外」と「不調」を分ける

最初の版は、ファイルを開くときの例外をすべて「ドライブの外」として返していました。

try {
  item = DriveApp.getFileById(id);
} catch (err) {
  return { found: false }; // 権限なしも Drive の一時的な不調も、全部ここに落ちる
}

これだと Drive が一時的に不調になっただけで、全員が「外にある」と判定されて登録できなくなります。例外メッセージで見分ける手もありますが、メッセージの言語は実行アカウントの設定で変わるので頼りにくいです。

そこで、ファイルが開けなかったら共有ドライブ本体を開き直して確かめます。

function canOpenDrive(driveId) {
  try {
    DriveApp.getFolderById(driveId).getId();
    return true;
  } catch (err) {
    return false;
  }
}

if (!canOpenDrive(SHARED_DRIVE_ID)) {
  return { ok: false, error: 'drive_unavailable' }; // 不調。判定しない
}
return { ok: true, found: false };                   // ドライブは開ける=ファイルは外

呼び出し側では「判定できない」が返ったら登録を止めずに通すことにしました。チェックの仕組みが落ちている間に、正しく置いた人まで締め出すほうが困るからです(置き場所を厳守させたい用途なら逆に止めるべきです)。

深さの上限は呼び出し元のタイムアウトから決める

getParents() は1段ごとに Drive への問い合わせになります。手元の計測では、ファイル1件の判定に Web アプリ全体で 1.4〜3.0 秒かかりました。呼び出し元のタイムアウトは 8 秒です。

当初は上限 40 段にしていましたが 8 秒を超えうるので 20 段に下げ、さらに上限到達時に false を返していたのを null に直しました。深い階層に正しく置いたファイルが、深いというだけで拒否されるのを避けるためです。

ちなみに、スクリプトを更新した直後の初回だけ 8 秒を超えたことがありました。その後は 2.3〜3.0 秒に戻ったので、更新直後の初回は遅いことがあると見込んでおくと安心です。

resourcekey 付きの古い共有リンク

古くからリンク共有されているファイルは、URL に resourcekey= が付いていることがあり、これを渡さないと getFileById() で開けません。リンクから ID とキーを両方取り出し、キーがあれば専用のメソッドを使います。

function getItem(id, resourceKey) {
  const getters = resourceKey
    ? [() => DriveApp.getFileByIdAndResourceKey(id, resourceKey),
       () => DriveApp.getFolderByIdAndResourceKey(id, resourceKey)]
    : [() => DriveApp.getFileById(id),
       () => DriveApp.getFolderById(id)];
  for (const get of getters) {
    try { return get(); } catch (err) { /* 次を試す */ }
  }
  return null;
}

別解:Drive API の driveId

今回は使っていませんが、Drive API(拡張サービス)のファイル情報には driveId があり、共有ドライブ内の項目にだけ入ります。1回の問い合わせで済むので、階層が深いファイルや判定回数が多い場合はこちらが素直です。

const meta = Drive.Files.get(fileId, { fields: 'driveId', supportsAllDrives: true });
const inDrive = meta.driveId === SHARED_DRIVE_ID;

まとめ

やりたいこと・起きること 結果・原因 対処
共有ドライブのファイルで getSharingAccess() メンバー全員が開けても PRIVATE 共有範囲では判定しない
共有ドライブのファイルで getOwner() null(組織の所有) オーナーのドメインでは判定しない
特定の共有ドライブの中か知りたい ドライブ ID=最上位フォルダ ID、親は1つ getParents() でさかのぼって比較
ファイルが開けない 「外」と「不調」が区別できない ドライブ本体が開けるか再確認
階層が深い 1段ごとに問い合わせ 上限を決め、到達時は「判定できない」
古い共有リンクが開けない resourcekey 付き getFileByIdAndResourceKey()
1回で判定したい — Drive API の driveId

参考: Google Drive API「共有ドライブの概要」/Drive API の drives・files リソース/Apps Script リファレンス「Class File」

この件は、AI(Claude Code)と一緒に作業した記録をもとにしたブログ記事でも書いています: https://kujiragames.com/2026/10/gas-shared-drive-check/

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?