GASでGoogleドライブの
ファイル・フォルダを一覧・検索する方法

「特定フォルダのファイルを全部リスト化したい」「名前や種類で絞って探したい」を、DriveAppのイテレータと検索クエリで実現する方法を、動くコードで解説します。

|対象: GAS / Googleドライブ / ファイル検索

Table of Contents

DriveAppのイテレータという考え方

GASからGoogleドライブを操作するのはDriveApp(ドライブを扱う組み込みサービス)です。ここで最初につまずきやすいのが、getFiles()が「配列」ではなく「イテレータ(順番に取り出す入れ物)」を返す点です。

ドライブには数万件のファイルが入っていることも珍しくありません。全部を一度に配列へ読み込むと重くなるため、GASはFileIteratorを返し、必要な分だけ順番に取り出せるようにしています。基本の形は次のとおりです。

hasNext()

次のファイル(フォルダ)がまだあるかを true / false で返す

next()

次のファイル(フォルダ)を1件取り出す。取り出すたびに次へ進む

getFiles()

フォルダ直下のファイルのイテレータを返す。サブフォルダは含まない

getFolders()

フォルダ直下のサブフォルダのイテレータを返す

フォルダ内のファイルを一覧取得する

まずは特定フォルダの直下にあるファイルを、名前とURLで一覧表示する基本形です。フォルダはgetFolderById()で取得します。フォルダIDは、ドライブでフォルダを開いたときのURL末尾の文字列です。

function listFilesInFolder(folderId) {
  // フォルダIDはドライブでフォルダを開いたときのURL末尾の文字列
  const folder = DriveApp.getFolderById(folderId);
  const files = folder.getFiles(); // FileIterator(配列ではない)

  while (files.hasNext()) {
    const file = files.next();
    Logger.log(
      file.getName() + " / " + file.getUrl()
    );
  }
}

whileでループを回し、hasNext()falseになるまで1件ずつ処理するのが定番の書き方です。ドライブ全体を対象にしたいときは、フォルダの代わりにDriveApp.getFiles()を使います。

名前・種類で絞り込んで取得する

「PDFだけ」「この名前のファイルだけ」を取り出したいときは、専用メソッドが用意されています。種類で絞るならgetFilesByType()、名前で探すならgetFilesByName()です。種類の指定にはMimeType(ファイル形式を表す定数)を使います。

function listPdfFiles(folderId) {
  const folder = DriveApp.getFolderById(folderId);
  // 種類で絞る: PDFだけ取得
  const files = folder.getFilesByType(MimeType.PDF);

  const result = [];
  while (files.hasNext()) {
    const file = files.next();
    result.push(file.getName());
  }

  // 名前で探すときは getFilesByName("請求書.pdf") も使える
  Logger.log("PDF: " + result.length + "件");
  return result;
}

MimeType.GOOGLE_SHEETSでスプレッドシート、MimeType.GOOGLE_DOCSでドキュメント、MimeType.JPEGで画像など、形式に応じた定数が使えます。どちらのメソッドもイテレータを返すので、取り出し方はgetFiles()と同じです。

検索クエリで柔軟に探す(searchFiles)

「名前に『請求書』を含むPDF」のように複数条件を組み合わせたいときはsearchFiles()を使います。引数にはGoogleドライブの検索構文で書いたクエリ(文字列)を渡します。

function searchInvoices(folderId) {
  const folder = DriveApp.getFolderById(folderId);

  // 検索クエリ = Googleドライブの検索構文
  //   title contains '...'   … ファイル名に含む
  //   mimeType = '...'        … 種類
  //   and / or で条件を組み合わせる
  const query =
    "title contains '請求書' and mimeType = 'application/pdf'";
  const files = folder.searchFiles(query);

  while (files.hasNext()) {
    const file = files.next();
    Logger.log(file.getName());
  }
}

よく使う条件はtitle contains '...'(名前に含む)、mimeType = '...'(種類)、modifiedDate > '...'(更新日)の3つです。and/orでつなげます。クエリ内の値はシングルクォートで囲む点に注意してください。

サブフォルダも含めて再帰的に全件取得する

getFiles()が返すのはフォルダ直下のファイルだけです。サブフォルダの中まで全部たどるには、getFolders()でサブフォルダを取得し、同じ処理を自分自身で繰り返す「再帰」を使います。

function listAllFiles(folderId) {
  const folder = DriveApp.getFolderById(folderId);
  const rows = [];
  collect(folder, rows);
  Logger.log("合計 " + rows.length + " ファイル");
  return rows;
}

// フォルダを再帰的にたどって全ファイルを集める
function collect(folder, rows) {
  const files = folder.getFiles();
  while (files.hasNext()) {
    const file = files.next();
    rows.push([file.getName(), file.getUrl(), folder.getName()]);
  }

  // 直下のサブフォルダを取得し、同じ処理を繰り返す
  const subFolders = folder.getFolders();
  while (subFolders.hasNext()) {
    collect(subFolders.next(), rows);
  }
}

collect()が、まず直下のファイルを集め、続いてサブフォルダごとに自分を呼び出す構造です。これで階層がどれだけ深くても全ファイルを1つの配列にまとめられます。ただしフォルダ数・ファイル数が多いと処理が長くなるため、次章以降の絞り込みと組み合わせるのがおすすめです。

更新日・サイズで絞り込む

「最近30日で更新されたファイルだけ」を取り出す例です。更新日はmodifiedDateで絞り込み、各ファイルのサイズはgetSize()(バイト単位)で取得します。

function listRecentFiles(folderId) {
  const folder = DriveApp.getFolderById(folderId);

  // 30日前の日付を ISO形式(yyyy-MM-dd)で作る
  const since = new Date();
  since.setDate(since.getDate() - 30);
  const isoDate = Utilities.formatDate(
    since, "Asia/Tokyo", "yyyy-MM-dd"
  );

  // modifiedDate > '日付' で更新日が新しいものだけに絞る
  const query = "modifiedDate > '" + isoDate + "'";
  const files = folder.searchFiles(query);

  while (files.hasNext()) {
    const file = files.next();
    // getSize() はバイト単位。1024で割ってKB表示にする
    const kb = Math.round(file.getSize() / 1024);
    Logger.log(
      file.getName() + " / " +
      Utilities.formatDate(
        file.getLastUpdated(), "Asia/Tokyo", "yyyy/MM/dd"
      ) + " / " + kb + "KB"
    );
  }
}

日付はUtilities.formatDate()yyyy-MM-dd形式の文字列にしてからクエリに埋め込みます。タイムゾーンにAsia/Tokyoを指定しないと日付がずれることがあるので、必ず指定しましょう。

一覧をスプレッドシートに書き出す

取得した一覧は、そのままスプレッドシートに書き出すと管理表として便利です。ポイントは、1件ずつ書き込まず、二次元配列にためてsetValues()で一括書き込みすること。ファイル数が多いほど速度差が大きくなります。

function exportFileListToSheet(folderId) {
  const sheet = SpreadsheetApp.getActiveSheet();
  const folder = DriveApp.getFolderById(folderId);
  const files = folder.getFiles();

  const rows = [["ファイル名", "種類", "更新日", "サイズ(KB)", "URL"]];
  while (files.hasNext()) {
    const file = files.next();
    rows.push([
      file.getName(),
      file.getMimeType(),
      Utilities.formatDate(
        file.getLastUpdated(), "Asia/Tokyo", "yyyy/MM/dd"
      ),
      Math.round(file.getSize() / 1024),
      file.getUrl(),
    ]);
  }

  // 1件ずつ書くと遅い。二次元配列で一括書き込みする
  sheet.clearContents();
  sheet.getRange(1, 1, rows.length, rows[0].length).setValues(rows);
}

これでファイル名・種類・更新日・サイズ・URLが並んだ一覧表が作れます。時間主導トリガーと組み合わせれば、フォルダの中身を定期的に棚卸しする仕組みにもできます。

実務での注意点

1. getFiles()は「順序が保証されない」

イテレータが返すファイルの並び順は保証されません。名前順や日付順で処理したい場合は、いったん配列に集めてからJavaScriptのsort()で並べ替えてください。

2. 同名ファイル・ゴミ箱内のファイルに注意

ドライブは同じ名前のファイルを複数作れます。getFilesByName()が複数件を返すこともあるため、IDで一意に扱うのが安全です。また削除済み(ゴミ箱内)のファイルが検索に含まれることがあるので、必要ならgetLastUpdated()や場所で除外します。

3. ファイル数が多いと6分の実行時間制限に届く

GASの1回の実行は最長6分です。全ドライブを再帰でたどると制限に達することがあります。searchFilesのクエリで対象を絞る、getContinuationToken()で処理を中断・再開する、といった分割実行を検討しましょう。

まとめ

GASでのドライブ操作は、getFiles()が返すイテレータをhasNext()/next()で回すのが基本です。種類での絞り込みはgetFilesByType()、複数条件はsearchFiles()、階層をまたぐなら再帰、と使い分ければ、ドライブの棚卸しや自動整理を無理なく仕組み化できます。

よくある質問

フォルダを取得してからgetFiles()を呼び、返ってきたイテレータをwhile (files.hasNext()) { const file = files.next(); ... }で回すのが基本です。getFiles()は配列ではなくFileIteratorを返すため、次のファイルがあるかをhasNext()で確認しながらnext()で1件ずつ取り出します。

配列ではありません。DriveApp.getFiles()やフォルダのgetFiles()はFileIterator(イテレータ)を返します。ドライブには数万件のファイルがあることも多く、全件を一度に配列へ読み込むと重くなるため、必要な分だけ順番に取り出せるイテレータになっています。for...ofやmapは直接使えないので、hasNext()とnext()のループで処理します。

名前で探すならgetFilesByName("報告書.pdf")、種類で探すならgetFilesByType(MimeType.PDF)が使えます。より柔軟に検索したい場合はsearchFiles("title contains '請求書' and mimeType = 'application/pdf'")のように検索クエリを渡します。クエリはGoogleドライブの検索構文に従い、title・mimeType・modifiedDateなどの条件を and / or で組み合わせられます。

できます。ただしgetFiles()は直下のファイルしか返さないため、getFolders()でサブフォルダを取得し、再帰的に自分自身を呼び出す関数を書く必要があります。フォルダ階層が深いと処理件数が増えるので、実行時間制限(1回あたり最長6分)に注意してください。

検索クエリで対象を絞る、continuation token(getContinuationToken)で処理を中断・再開する、PropertiesServiceに進捗を保存して次回の起動で続きから処理する、といった分割実行が有効です。まずはsearchFilesのクエリで更新日や種類を限定し、処理対象そのものを減らすのが効果的です。

GAS開発・業務Webシステムを
相談する。

ドライブの自動整理やファイル棚卸しのほか、帳票出力・Gmail自動化など、日々の業務を自動化するGAS開発をご相談いただけます。

GAS Webシステム開発を見る