ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【GAS/Googleスプレッドシート】Googleドライブのフォルダ内のファイル一覧とファイルリンク、最終更新日時を取得する

 Googleドライブのとあるフォルダでファイルを管理しようと思っていたのですが、煩雑になりがちなのでフォルダ内のファイル一覧のスプレッドシートが欲しいと思うようになってきました。
 その一覧のファイルでファイルへのリンクと最終更新日時とかをGoogle Apps Script(GAS)の処理で簡単に見れるようにしたいと色々調べて試行錯誤したところ、実現できたので今回はその備忘録になります。


 では、始めます。


1:新規にスプレッドシートを作成する
 どこでも良いので、Googleドライブ上で右クリックして「Googleスプレッドシート」をクリックして作成します。

 何でもよいですが、ここでは新規作成したスプレッドシートの名を「ファイル一覧取得」として開き、以下のように表のひな形を作成します。各表の中身は空にしておきます。

 最終更新日時の列のデータ部分は以下のように「日時」の表示にしておきます。

 これでGASを実行して入力する表ができました。


2:Drive API ドキュメントのサービスを追加する
 表のひな形を作ったら、スプレッドシートのメニューにある「拡張機能」→「Apps Script」をクリックします。

 無題のプロジェクトのGASの画面が開くので、何でもよいですがここではプロジェクト名を「ファイル一覧取得」として「名前を変更」をクリックします。

 次に今回必要なサービスを追加します。左側メニューの「サービス」と書かれている横にある「+」をクリックします。

 サービスを追加の画面が表示されるので「Drive API ドキュメント」を選択した状態で「追加」ボタンをクリックします。

 サービスの下に「Drive」が表示されていれば、サービスの追加はできています。


3:GASを記述する
 サービスを追加できたら、左側メニューにある「コード.gs」をクリックし、以下のスクリプトを記述します。

// 定数
const FOLDER_URL_ROW = 1; // フォルダURL行番号
const FOLDER_URL_COL = 2; // フォルダURL列番号
const START_ROW = 4;      // データ入力開始行番号
const START_COL = 1;      // データ入力開始列番号
const NO_FILE_CELL = -1;  // ファイル名が見つからなかった時の行番号


// スプレッドシートを開いたときに実行される関数
function onOpen() {
  // ファイルをOpenした時にメニューを追加
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var menuEntries = [];

  menuEntries.push({name: "ファイル一覧取得", functionName: "getFilesInfo"});

  // メニューを追加
  ss.addMenu("追加メニュー", menuEntries);
}

// 構造体(ファイル名, リンクURL, 最終更新日時)
function fileInfo(name, link, lastUpdate) {
  this.name = name;
  this.link = link;
  this.lastUpdate = lastUpdate;
}

// ファイル情報を取得して対象シートに記入する関数
function getFilesInfo() {
  let fileNames = [];
  let fileLinks = [];
  let fileLastUpdate = [];

  // シートを取得
  let ss = SpreadsheetApp.getActiveSpreadsheet();
  let activeSheet = ss.getActiveSheet();

  // フォルダのリンクからフォルダのIDを取得
  let folderLink = activeSheet.getRange(FOLDER_URL_ROW, FOLDER_URL_COL).getValues()[0][0];
  let split = folderLink.split('/');
  let folderId = split[split.length-1];

  // ファイル一覧を取得(降順)
  let files = DriveApp.getFolderById(folderId).getFiles();

  // ファイル名、URL、最終更新日を取得
  while (files.hasNext()) {
    let file = files.next();
    fileNames.push(file.getName());
    fileLinks.push(file.getUrl());
    fileLastUpdate.push(file.getLastUpdated());
  }

  // ファイルの個数を取得
  const size = fileNames.length;

  // 行を追加して履歴をコピー
  activeSheet.insertColumnAfter(START_COL+2);
  const copyRange = activeSheet.getRange(START_ROW-1, START_COL+2, activeSheet.getMaxRows(), 1);
  const destination = activeSheet.getRange(START_ROW-1, START_COL+3, activeSheet.getMaxRows(), 1);
  copyRange.copyTo(destination, SpreadsheetApp.CopyPasteType.PASTE_NORMAL);
  activeSheet.getRange(START_ROW-1, START_COL+3).setValue("ファイル更新履歴");

  // 最終行を取得(対象行の最終行から上に検索して最初に出てきたデータ行)
  let lastRow = activeSheet.getRange(activeSheet.getMaxRows(), START_COL).getNextDataCell(SpreadsheetApp.Direction.UP).getRow();

  // セルに記入
  for (let i = size-1; i >= 0; i--) {
    let targetRow = getFileNameRow(activeSheet, lastRow, fileNames[i]);
    if (targetRow == NO_FILE_CELL) {
      // 新規ファイルの場合は行を追加
      lastRow++;
      activeSheet.getRange(lastRow, START_COL).setValue(fileNames[i]);
      activeSheet.getRange(lastRow, START_COL+1).setValue(fileLinks[i]);
      activeSheet.getRange(lastRow, START_COL+2).setValue(fileLastUpdate[i]);
    } else {
      // 既存ファイルの場合はURLと更新日時を更新
      activeSheet.getRange(targetRow, START_COL+1).setValue(fileLinks[i]);
      activeSheet.getRange(targetRow, START_COL+2).setValue(fileLastUpdate[i]);
    }
  }

  SpreadsheetApp.getUi().alert("記入処理が終了しました。");
}

// ファイル名の行を取得する関数
function getFileNameRow(activeSheet, lastRow, filename) {
  for (let i = START_ROW; i <= lastRow; i++) {
    let cellValue = activeSheet.getRange(i, START_COL).getValue();
    if (cellValue == filename) {
      return i;
    }
  }
  return NO_FILE_CELL;
}

 詳しくは内容を読んでいただきたいのですが、簡単に説明します。

 onOpen()関数でスプレッドシートのファイルが開いたとき、スプレッドシートの上メニュー部分に「追加メニュー」というのを追加しています。そしてその中の「ファイル一覧取得」をクリックするとメインとなるスクリプトが実行されるように設定しています。

 getFilesInfo()がメインとなる関数で、フォルダのURLからID部分だけを抜き出してファイル一覧を取得しています。「let files = DriveApp.getFolderById(folderId).getFiles();」でファイル一覧を取得しているのですが、どうやら降順に取得しているようなので最後の記入するところで逆から処理するようにしています。
 ファイル名、URL、最終更新日時を取得した後、最終更新日時の列を丸ごとコピーして一つとなりの行に追加しています。このようにすることでファイルの更新履歴も追えるようにしています。
 更新日時列のコピー後、新規ファイルであれば新しく行を追加、既存のものはURLと更新日時を更新、という処理にしています。

 またエラー処理は細かくやっていないので、そのあたりは注意してください。


4:実際に実行する
 3のスクリプトを記述できたら、一度スプレッドシート自体を開き直します。

 フォルダURLのセル(B1)に一覧として表示させたいURLを記入します。以下のようなxxxx...となる部分が固有の値になっているものをコピペして入力してください。

https://drive.google.com/drive/folders/xxxxxxxxxxxxxxxxxxxxxxxxxxx

 入力できたら今回GASで追加した「追加メニュー」→「ファイル一覧取得」をクリックします。

 初回の場合は以下のように承認が必要になるので「承認」をクリックします。

 クリックすると以下のようにアカウント一覧が表示されるので、承認したいアカウントを選択します。

 このアプリはGoogleで確認されていません、という画面が出てくるので左下の「詳細」をクリックします。

 「ファイル一覧取得(安全ではないページ)に移動」をクリックします。

 許可を求める画面になるので、確認して「許可」をクリックします。

 これで使用できるようになったので、もう一度「追加メニュー」→「ファイル一覧取得」をクリックして実行します。

 スクリプトがエラーなく終了すると、以下の画面が出てくるので「OK」をクリックします。

 以下のようにファイル一覧とURL、最終更新日時が記入されています。

 ちなみに今回対象としたGoogleドライブの中身は以下のようになっています。対象フォルダ内にあるフォルダは対象外となっています。


 以上が、GASでGoogleドライブのフォルダ内のファイル一覧とファイルリンク、最終更新日時を取得する方法になります。

 例えば定期的なcsvなどのファイル一覧をこのように管理する、というのも一つの方法だと思います。
 色々と使える幅は広いように感じているので、この記事を読んでいただいた方の参考になれば幸いです。


・参考資料