How do I extend script execution time?

2020-03-08 06:33发布

问题:

I have this script to delete all ._* files from my Google Drive. But there are millions of files, and the script quits after 5 minutes or so with "Exceeded maximum execution time."

function TrashDotFiles() {
  var files = DriveApp.getFiles();
  while (files.hasNext()) {
    var file = files.next();
    var name = file.getName();
    if (name.indexOf("._")==0) {
      console.log(name + " • trashed");
      file.setTrashed(true)
    }
  }
}

How can I let this script run long enough to scan the entire Drive?

回答1:

One of the possible workarounds is to run plain JavaScript code in modal window that has no limit of running time:

function onOpen()
{
  var ui = SpreadsheetApp.getUi();

  ui.createMenu('Trash dot files')
      .addItem('Run', 'openWindow')
      .addToUi();
}

function openWindow()
{
  var ui = SpreadsheetApp.getUi();

  // get template
  var template = HtmlService.createTemplateFromFile('deleteDriveFiles');

  // need to have next line of text somewhere (even commented out) to trigger correct scopes for script and token:
  // DriveApp.getFiles()

  // pass token
  template.data = {
    token: ScriptApp.getOAuthToken()
  };

  // get output html
  var html = template.evaluate();

  // show modal window
  ui.showModalDialog(html, 'Delete files from Drive');
}

[File - New - Html file] deleteDriveFiles.html:

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
  <script>

  // used for visual log
  function addParagraph(text)
  {
    var node = document.createElement('p');
    node.innerText = text;
    document.body.appendChild(node);
  }

  // for stats
  var totalProcessed = 0;
  var totalTrashed = 0;

  function reqListener(){
    // get response obj
    var res = JSON.parse(this.responseText);

    if (res.items && res.items.length)
    {
      // loop files
      for (var i = 0; i < res.items.length; i++)
      {
        var file = res.items[i];

        // delete file
        if (file.title.indexOf('._') == 0)
        {
          var xhr = new XMLHttpRequest();
          xhr.addEventListener('load', function(){
            // get response obj
            var res = JSON.parse(this.responseText);

            // sucessfully trashed
            if (this.status == 200)
            {
              totalTrashed++;
              addParagraph('Trashed '+res.title);
            }
          }.bind(xhr));
          xhr.open('POST', 'https://www.googleapis.com/drive/v2/files/'+file.id+'/trash');
          xhr.setRequestHeader('Authorization', 'Bearer <?=data.token?>');
          xhr.send();
        }
      }

      // for stats
      totalProcessed += res.items.length;
    }

    // get next page of results
    if (res.nextPageToken)
    {
      var xhr = new XMLHttpRequest();
      xhr.addEventListener('load', reqListener);
      xhr.open('GET', 'https://www.googleapis.com/drive/v2/files?trashed=false&pageToken='+res.nextPageToken);
      xhr.setRequestHeader('Authorization', 'Bearer <?=data.token?>');
      xhr.send();
    }
    // finished
    else
    {
      addParagraph('Finished. Processed total: '+totalProcessed+'. Trashed total: '+totalTrashed);
    }
  }

  var xhr = new XMLHttpRequest();
  xhr.addEventListener('load', reqListener);
  xhr.open('GET', 'https://www.googleapis.com/drive/v2/files?trashed=false');
  xhr.setRequestHeader('Authorization', 'Bearer <?=data.token?>');
  xhr.send();

  </script>
  </body>
</html>

After saving changes in code and refreshing spreadsheet web page you will see new menu item:

Click on Run to start processing files.

Result: