SANANEBLOG
GAS PR

Google Driveのファイルやフォルダの新規追加・変更・削除ログを記録するツール【GAS】

記事内に商品プロモーションを含む場合があります

はじめに

今回の記事では、Google Apps Script(GAS)を使用してGoogle Drive上のファイルやフォルダの変更ログをスプレッドシートに記録できるツールを紹介します。

Google Drive大量のファイルやフォルダが存在する場合、特定のアイテムがいつ、誰によって変更されたのかを追跡するのは一筋縄ではいきません。

そこで今回の記事ではGoogle Apps Scriptを使用して、Google Drive内のファイルやフォルダの変更履歴を定期的にトラッキングし、Googleスプレッドシートに記録する方法を紹介します。

今回の自動化で得られるメリット
  1. 手動でのチェックが不要。定期的にスクリプトを実行するだけで、変更履歴が自動的に記録されます。
  2. 新規作成、編集、削除されたアイテムの情報だけでなく、変更を行ったユーザ(メールアドレス)まで取得可能。
  3. Googleスプレッドシートに変更履歴がまとめられるため、過去の変更を簡単に確認できます。
SANANE

GASを触ったことのない方でも簡単に作成可能です。作業時間は15分ほどです。

GASの実行時間の上限(6分)という仕様上、本ツールで処理できるファイルやフォルダ数は約1000〜2000ファイルほどを想定しています。それ以上のファイルはタイムアウトにより途中で処理が終了する場合があります。

動作イメージ

SANANE

まずは簡単に本ツールの動作について説明します。

まず、記録を行いたいフォルダを用意します。今回は[test_Monitoring_Drive]としています。

また、[サンプルフォルダ]内にも画像のようにファイルが存在しています。今回のツールではサブフォルダ以下のファイルやフォルダに対しても記録が可能です。

ツールとなるスプレッドシートは以下のようになっています。A1セルには記録を行った最終実行時間が記載されており、また、もう一つ別のシートにはファイルのIDを記録するためのシートを作成しています。

GASが実行されると、A1セルの最終実行時間よりも後に作成されたファイルに対しては[新規追加]として、スプレッドシートに記録されます。

また、ファイルの中身を変更したり、ファイルやフォルダの削除を行うとその記録が追記されます。

本ツールでは削除したファイルのIDは表示されますが、それ以外はすでに削除済みのため[不明]としています。

作成方法

スプレッドシートの新規作成

まず、Google スプレッドシートにアクセスします。

アクセスしたら、[新しいスプレッドシートを作成]> [空白]をクリックして新しいスプレッドシートを作成します。

また、以下のように[FileFolderLog]というシートを作成しておいてください。

また、スプレッドシートに2行目には画像のようにヘッダーを作成しておくと後で見やすくなります。

その後、[拡張機能]>[Apps Script]をクリックします。

GASの設定

新しいタブが開きGASが表示されます。

ここから、GAS上でスクリプトを作成します。もともと記載してある以下コードは削除します。

function myFunction() {

}

削除したら、以下コードをコピーしてそのまま貼り付けてくだい。

このとき、1行目のYOUR_FOLDER_ID には記録したいフォルダーのIDに書き換えます。

SANANE

フォルダIDは以下で確認できます。

①IDを知りたいフォルダを開く
②URLをチェックし『…/folders/{フォルダID}』をコピー

var FOLDER_ID = 'YOUR_FOLDER_ID'; // フォルダIDをスクリプトの一番上に記載
var LOG_SHEET_NAME = 'FileFolderLog';// ログのシート名を記載

function checkFilesAndFoldersChanges() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); 
  var sheet = spreadsheet.getActiveSheet();
  var logSheet = spreadsheet.getSheetByName(LOG_SHEET_NAME);
  
  var parentFolder = DriveApp.getFolderById(FOLDER_ID);  

  var lastCheckedDate = new Date(sheet.getRange('A1').getValue());
  var previousIds = getPreviousIds(logSheet);
  
  var changes = [];
  checkFolderRecursively(parentFolder, changes, lastCheckedDate, previousIds);

  // 削除されたアイテムを検出
  var currentIds = getCurrentIds(parentFolder);
  previousIds.forEach(function(id) {
    if (currentIds.indexOf(id) === -1) {
      // 削除されたアイテム
      changes.push([new Date(), '不明', '不明', '削除', '不明', id, '不明']);
    }
  });
  
  // 現在のIDのリストをログシートに保存
  saveCurrentIds(logSheet, currentIds);
  
  // 変更を一度にスプレッドシートに書き込む
  if (changes.length > 0) {
    var targetRange = sheet.getRange(sheet.getLastRow() + 1, 1, changes.length, 7);
    targetRange.setValues(changes);
  }
  
  sheet.getRange('A1').setValue(new Date()); // 最後にチェックした日時を更新
}

function checkFolderRecursively(folder, changes, lastCheckedDate, previousIds) {
  var files = folder.getFiles();
  var folders = folder.getFolders();
  
  while (files.hasNext()) {
    var file = files.next();
    var createdDate = new Date(file.getDateCreated());
    var updatedDate = new Date(file.getLastUpdated());
    var userEmail = getLastModifiedUser(file.getId());
    
    if (createdDate > lastCheckedDate) {
      changes.push([file.getLastUpdated(), 'ファイル', file.getName(), '新規追加', userEmail, file.getId(), file.getParents().next().getId()]);
    } else if (updatedDate > lastCheckedDate) {
      changes.push([file.getLastUpdated(), 'ファイル', file.getName(), '変更', userEmail, file.getId(), file.getParents().next().getId()]);
    }
  }
  
  while (folders.hasNext()) {
    var subFolder = folders.next();
    var createdDate = new Date(subFolder.getDateCreated());
    var updatedDate = new Date(subFolder.getLastUpdated());
    var userEmail = getLastModifiedUser(subFolder.getId()); // フォルダの最後の変更を行ったユーザのメールアドレスを取得
    
    if (createdDate > lastCheckedDate) {
      // 新規作成されたフォルダ
      changes.push([subFolder.getLastUpdated(), 'フォルダ', subFolder.getName(), '新規追加', userEmail, subFolder.getId(), subFolder.getParents().next().getId()]);
    } else if (updatedDate > lastCheckedDate) {
      // 編集されたフォルダ
      changes.push([subFolder.getLastUpdated(), 'フォルダ', subFolder.getName(), '変更', userEmail, subFolder.getId(), subFolder.getParents().next().getId()]);
    }
    
    // サブフォルダを再帰的にチェック
    checkFolderRecursively(subFolder, changes, lastCheckedDate, previousIds);
  }
}


function writeToFileSheet(sheet, lastUpdated, type, name, id, parentId, action, useremail) {
  var lastRow = sheet.getLastRow();
  var targetRow = lastRow + 1;

  sheet.getRange('A' + targetRow).setValue(lastUpdated);
  sheet.getRange('B' + targetRow).setValue(type);
  sheet.getRange('C' + targetRow).setValue(name);
  sheet.getRange('D' + targetRow).setValue(action);
  sheet.getRange('E' + targetRow).setValue(useremail);
  sheet.getRange('F' + targetRow).setValue(id);
  sheet.getRange('G' + targetRow).setValue(parentId);
}

function getPreviousIds(logSheet) {
  var lastRow = logSheet.getLastRow();
  if (lastRow === 0) {
    return [];
  }
  var data = logSheet.getRange(1, 1, lastRow).getValues();
  return data.map(function(row) { return row[0]; });
}

function getCurrentIds(folder) {
  var ids = [];
  var files = folder.getFiles();
  var folders = folder.getFolders();
  
  while (files.hasNext()) {
    ids.push(files.next().getId());
  }
  
  while (folders.hasNext()) {
    var subFolder = folders.next();
    ids.push(subFolder.getId());
    ids = ids.concat(getCurrentIds(subFolder));
  }
  return ids;
}

function saveCurrentIds(logSheet, ids) {
  logSheet.clear(); // 以前のデータをクリア
  var data = ids.map(function(id) { return [id]; });
  logSheet.getRange(1, 1, data.length, 1).setValues(data);
}

function getLastModifiedUser(fileId) {
  try {
    var revisions = Drive.Revisions.list(fileId);
    if (revisions && revisions.items && revisions.items.length > 0) {
      var lastRevision = revisions.items[revisions.items.length - 1];
      return lastRevision.lastModifyingUser.emailAddress;
    }
  } catch (e) {
    // エラーが発生した場合
    return null;
  }
  return null;
}

アプリIDとスプレッドシートIDの記入が完了したら、[Ctrl+S]かApps Script 上部バーの[プロジェクトを保存]をクリックして保存します。

Drive APIの追加

Apps Scriptのエディタの左サイドバーにある「サービス」をクリックします。

「+ サービスを追加」をクリックし、”Drive API”を検索して追加します。

GASの実行

Google Apps Script 上部バーの[▶実行]をクリックします。最初のスクリプトの実行には権限が必要となるため、[承認が必要です]というモーダルが表示されたら、

[権限を確認]>[表示されているGoogleアカウント]>[詳細]>[無題のプロジェクト(安全ではないページ)に移動]>[許可]まで移動します。クリック後GASが実行されます。

この時点では、A1セルに実行した時間が記載されます。

トリガーの作成

トリガーを設定することで、スクリプトを定期的に自動実行することができます。例えば、毎日特定の時間に変更履歴をチェックするように設定することが可能です。

Apps Scriptのエディタの左サイドバーにある「トリガー」(時計のアイコン)をクリックし、下部にある「+ トリガーを追加」をクリックします。

「関数を実行」のドロップダウンから、checkFilesAndFoldersChangesを選択します。

「イベントのソース」のドロップダウンから「時間主導型」を選択します。

「時間ベースのトリガーのタイプ」のドロップダウンから、実行の頻度を選択します。

必要に応じて他のオプションを設定し、「保存」をクリックします。

注意点

Google Apps Scriptには実行時間の上限があり、1回の連続実行は最大6分までです。今回のツールで想定しているファイル数は1000~2000程度であり、それ以上のファイルを記録する場合は、スクリプトが途中で停止する場合があります。

トリガーの追加や、実行などでエラーが発生した場合はスクリプトがうまく保存されていない場合があります。一度スクリプトをコピーしてからブラウザの更新などを行ってください。また、シート名やフォルダIDが間違っていないかも改めて確認してください。

GASおすすめ本

GASをもっと勉強したい!ほかにも自分で何か作成したい!という方には以下がおすすめです。仕事で使えるアイデアなども得ることができます。