使用不同延续令牌构建的迭代器在Google Apps中产生相同的结果

时间:2014-06-23 22:57:33

标签: google-apps-script google-sheets google-drive-api

我正在电子表格中编写Google Apps脚本。我的用例包括迭代一组给定的子文件夹。问题是处理时间超过了Google允许的最长时间(6分钟),因此我必须对脚本进行编程才能以后恢复。我正在创建一个触发器来恢复任务,但这不是我问题的一部分(至少,目前不是更重要的一个)。

我的代码看起来像这样(减少到最低限度来说明我的问题):

function launchProcess() {
    var scriptProperties = PropertiesService.getScriptProperties();
    scriptProperties.setProperty(SOURCE_PARENT_FOLDER_KEY, SOURCE_PARENT_FOLDER_ID);
    scriptProperties.deleteProperty(CONTINUATION_TOKEN_KEY);
    continueProcess();
}

function continueProcess() {
    try {
        var startTime = (new Date()).getTime();
        var scriptProperties = PropertiesService.getScriptProperties();
        var srcParentFolderId = scriptProperties.getProperty(SOURCE_PARENT_FOLDER_KEY);
        var continuationToken = scriptProperties.getProperty(CONTINUATION_TOKEN_KEY);
        var iterator = continuationToken == null ? DriveApp.getFolderById(srcParentFolderId).getFolders() : DriveApp.continueFolderIterator(continuationToken);

        var timeLimitIsNear = false;
        var currTime;
        while (iterator.hasNext() && !timeLimitIsNear) {
            var folder = iterator.next();

            processFolder_(folder);

            currTime = (new Date()).getTime();
            timeLimitIsNear = (currTime - startTime >= MAX_RUNNING_TIME);
        }

        if (!iterator.hasNext()) {
            scriptProperties.deleteProperty(CONTINUATION_TOKEN_KEY);
        } else {
            var contToken = iterator.getContinuationToken();
            scriptProperties.setProperty(CONTINUATION_TOKEN_KEY, contToken);
        } 

    } catch (e) {
        //sends a mail with the error
    }    
}

当调用launchProcess时,它只为另一个迭代文件夹的方法continueProcess准备程序。迭代器是通过使用延续令牌获得的,当它存在时(它不会出现在第一次调用中)。当时间限制接近时,continueProcess获取延续令牌,将其保存在属性中并等待下一次调用。

我遇到的问题是迭代器总是返回同一组文件夹,尽管它是由不同的标记构建的(我已经打印过它们,所以我知道它们是不同的)。

关于我做错了什么的任何想法?

提前谢谢。

4 个答案:

答案 0 :(得分:2)

看来你的循环没有正确构建。 (编辑:实际上,可能还有关于我们如何打破while循环的另一个问题,请参阅我在评论中对此的看法)

另请注意,在此上下文中使用try / catch没有特殊原因,因为我认为没有理由hasNext()方法会返回错误(但如果您认为这样,您可以随时添加它)< / p>

这是一个有效的示例,我添加了触发器创建/删除行来实现我的测试。

编辑:使用日志和计数器

更新的代码
var SOURCE_PARENT_FOLDER_ID = '0B3qSFd3iikE3MS0yMzU4YjQ4NC04NjQxLTQyYmEtYTExNC1lMWVhNTZiMjlhMmI'
var MAX_RUNNING_TIME = 5*35*6;

function launchProcessFolder() {
  var scriptProperties = PropertiesService.getScriptProperties();
  scriptProperties.setProperty('SOURCE_PARENT_FOLDER_KEY', SOURCE_PARENT_FOLDER_ID);
  scriptProperties.setProperty('counter', 0);
  scriptProperties.deleteProperty('CONTINUATION_TOKEN_KEY');
  ScriptApp.newTrigger('continueProcess').timeBased().everyMinutes(10).create();
  continueProcessFolder();
}

function continueProcessFolder() {
  var startTime = (new Date()).getTime();
  var scriptProperties = PropertiesService.getScriptProperties();
  var srcParentFolderId = scriptProperties.getProperty('SOURCE_PARENT_FOLDER_KEY');
  var continuationToken = scriptProperties.getProperty('CONTINUATION_TOKEN_KEY');
  var iterator = continuationToken == null ? DriveApp.getFolderById(srcParentFolderId).getFolders() : DriveApp.continueFolderIterator(continuationToken);
  var timeLimitIsNear = false;
  var currTime;
  var counter = Number(scriptProperties.getProperty('counter'));
  while (iterator.hasNext() && !timeLimitIsNear) {
    var folder = iterator.next();
    counter++;
    Logger.log(counter+'  -  '+folder.getName());

    currTime = (new Date()).getTime();
    timeLimitIsNear = (currTime - startTime >= MAX_RUNNING_TIME);

    if (!iterator.hasNext()) {
      scriptProperties.deleteProperty('CONTINUATION_TOKEN_KEY');
      ScriptApp.deleteTrigger(ScriptApp.getProjectTriggers()[0]);
      Logger.log('******************no more folders**************');
      break;
    }
  }
  if(timeLimitIsNear){
    var contToken = iterator.getContinuationToken();
    scriptProperties.setProperty('CONTINUATION_TOKEN_KEY', contToken);
    scriptProperties.setProperty('counter', counter);
    Logger.log('write to scriptProperties');
  }
}

编辑2:

(另见最后评论)

这是一个测试,修改了脚本以获取文件夹中的文件。从我的不同测试看来,操作非常快,我需要设置一个非常短的超时限制,以便在到达列表末尾之前实现它。

我添加了几个Logger.log()和一个counter,以确切了解发生了什么,并确定是什么中断了while循环。

使用当前值,我可以看到它按预期工作,第一次(和第二次)中断发生时间限制,并且记录器确认写入了令牌。在第三次运行中,我可以看到所有文件都已被转储。

var SOURCE_PARENT_FOLDER_ID = '0B3qSFd3iikE3MS0yMzU4YjQ4NC04NjQxLTQyYmEtYTExNC1lMWVhNTZiMjlhMmI'
var MAX_RUNNING_TIME = 5*35*6;

function launchProcess() {
  var scriptProperties = PropertiesService.getScriptProperties();
  scriptProperties.setProperty('SOURCE_PARENT_FOLDER_KEY', SOURCE_PARENT_FOLDER_ID);
  scriptProperties.setProperty('counter', 0);
  scriptProperties.deleteProperty('CONTINUATION_TOKEN_KEY');
  ScriptApp.newTrigger('continueProcess').timeBased().everyMinutes(10).create();
  continueProcess();
}

function continueProcess() {
  var startTime = (new Date()).getTime();
  var scriptProperties = PropertiesService.getScriptProperties();
  var srcParentFolderId = scriptProperties.getProperty('SOURCE_PARENT_FOLDER_KEY');
  var continuationToken = scriptProperties.getProperty('CONTINUATION_TOKEN_KEY');
  var iterator = continuationToken == null ? DriveApp.getFolderById(srcParentFolderId).getFiles() : DriveApp.continueFileIterator(continuationToken);
  var timeLimitIsNear = false;
  var currTime;
  var counter = Number(scriptProperties.getProperty('counter'));
  while (iterator.hasNext() && !timeLimitIsNear) {
    var file = iterator.next();
    counter++;
    Logger.log(counter+'  -  '+file.getName());

    currTime = (new Date()).getTime();
    timeLimitIsNear = (currTime - startTime >= MAX_RUNNING_TIME);

    if (!iterator.hasNext()) {
      scriptProperties.deleteProperty('CONTINUATION_TOKEN_KEY');
      ScriptApp.deleteTrigger(ScriptApp.getProjectTriggers()[0]);
      Logger.log('******************no more files**************');
      break;
    }
  }
  if(timeLimitIsNear){
    var contToken = iterator.getContinuationToken();
    scriptProperties.setProperty('CONTINUATION_TOKEN_KEY', contToken);
    scriptProperties.setProperty('counter', counter);
    Logger.log('write to scriptProperties');
  }
}

答案 1 :(得分:1)

截至2016年1月1日,这仍然是一个问题。错误报告使用Advanced Drive API which is documented here, under "Listing folders"列出了一个解决方案。

如果您不想使用高级服务,另一种解决方案是使用Folder Iterator创建一个File Ids数组。

在我看来,文件夹迭代器只有在使用DriveApp.continueFolderIterator()创建时才会出现问题。使用此方法时,返回的文件夹迭代器中只包含100个文件夹。

根据执行记录,使用DriveApp.getFolders()并且只获取文件夹ID,我能够在2.734秒内迭代694个文件夹。

function allFolderIds() {
  var folders = DriveApp.getFolders(),
      ids = [];
  while (folders.hasNext()) {
    var id = folders.next().getId();
    ids.push(id);
  }
  Logger.log('Total folders: %s', ids.length);
  return ids;
}

我使用返回的数组使用触发器在所有文件夹中工作。 Id数组太大而无法保存在缓存中,因此我创建了一个临时文件并使用缓存来保存临时文件ID。

答案 2 :(得分:0)

答案 3 :(得分:0)

似乎您只存储一个连续令牌。如果要递归遍历一组文件夹并允许脚本在任何时候暂停(例如,避免超时)并在以后恢复,则需要存储更多的连续令牌(例如,在对象数组中)

我已经outlined a template that you can use here使其正常运行。在30多个过程中,它可以处理数千个嵌套文件,完美运行。