2019年1月19日土曜日

Google Apps Script で指定フォルダ以下のファイルを処理する方法(3)

Google Driveで指定したフォルダ以下に配置されているすべてのファイルに対して処理を行うGoogle Apps Script(GAS)を考えてみた。普通に考えるとフォルダ内のファイルとフォルダを処理する関数を再帰的に呼び出すのだが、、、GASには6分以上経過すると強制的にスクリプトが終了してしまう制限がある。

この6分の壁対策として時間トリガを設定して中断した処理を再開する必要がある。しかしその再開のためにはどこまで処理したかを保存しなけれがならない。一般的には中断するときにFileIteratorクラスのgetContinuationToken()を使用してトークンを取得しそれをスクリプトプロパティなどに保存して、時間トリガで起動した際にはDriveAppクラスのcontinueFileIterator(continuationToken)でFileIteratorを復元する。

しかし、再帰的に関数を呼び出している処理を中断するのは少しコツがいる。

今回は単純な方法を紹介する。
  1. ドライブで管理しているすべてのFileIteratorを所得する
  2. FileIteratorですべてのファイルをスキャンする
  3. ファイルが指定したフォルダ配下かどうかをチェックする
  4. 5分経過した場合にはFileIteratorをプロパティに保存する
ただし、この方法には欠点がある。すべてのファイルで、親フォルダをスキャンするので処理が重く、FileIteratorは1週間という有効期限があるため、ドライブ内のすべての処理が1Week以内で終わるようにトリガタイミングと処理を調整しなければならない。

そのうち6分の壁対策版の再帰処理コードも載せると思う。

function start()
{
  // プロパティとトリガをすべて削除
  PropertiesService.getScriptProperties().deleteAllProperties();
  var triggers = ScriptApp.getProjectTriggers();
  for (var i = 0; i < triggers.length; i++) {
    ScriptApp.deleteTrigger(triggers[i]);
  }

  // トリガを設定する。(10分間隔)
  trigger = ScriptApp.newTrigger("triggerFunction")
  .timeBased()
  .everyMinutes(10)
  .create();
  triggerFunction();
}

function triggerFunction() {
  var StartTime   = new Date();
  var TargetFolder = "1zXo...処理対象のフォルダID....xEfEI";

  var ContinuationToken = PropertiesService.getScriptProperties().getProperty("ContinuationToken");
  if( ContinuationToken != null )
  {
    var files = DriveApp.continueFileIterator(ContinuationToken);
  }
  else
  {
    var files = DriveApp.getFiles();
  }

  while(files.hasNext()) {
    var CurrentTime = new Date();
    // トリガを設定する。(5分間実行していたら止める。)
    if( (CurrentTime - StartTime) > 5*60*1000 ){
      PropertiesService.getScriptProperties().setProperty("ContinuationToken",files.getContinuationToken());
      return;
    }
    var file = files.next();
    if( checkSubordinate( file, TargetFolder ) )
    {
      ////////////////////////////////////
      // ファイルごとの処理をここから記載する。

      // ★★ ここにfileに対する処理を記載する ★★

      // ファイルごとの処理をここまでに記載する。
      ////////////////////////////////////
    }
  }
  
  PropertiesService.getScriptProperties().deleteAllProperties();
  var triggers = ScriptApp.getProjectTriggers();
  for (var i = 0; i < triggers.length; i++) {
    ScriptApp.deleteTrigger(triggers[i]);
  }
}

// targetが指定フォルダ(folder ID)の配下のフォルダかチェックする。
function checkSubordinate(target,folderID)
{
  var parents = target.getParents();
  while( parents.hasNext() )
  {
    var parent = parents.next();
    var id = parent.getId();
    if( id === folderID ){
      return true;
    }
    if( checkSubordinate(parent,folderID) )
    {
      return true;
    }
  }
  return false;
}

0 件のコメント:

コメントを投稿

質問、要望、指摘など書いていただいてもよいですが、対応できるとは限りませんのでご了承ください。私に対するものも含め他の人を嫌な気分になるようなコメントは避けてください。