用于复制整个 Google Drive 文件结构的 Google Apps 脚本;如何避免超时?

Posted

技术标签:

【中文标题】用于复制整个 Google Drive 文件结构的 Google Apps 脚本;如何避免超时?【英文标题】:Google Apps Script To Copy Entire Google Drive File Structure; How To Avoid Timeouts? 【发布时间】:2015-05-28 02:16:46 【问题描述】:

我的单位正在切换到 Google 企业帐户,每个人都需要将他们的云端硬盘文件转移到他们的新帐户。 Drive 不允许在这些帐户之间转移所有权,因此我创建了一个脚本来将文件和文件夹从旧帐户复制到新帐户。 (旧帐户的内容已移至与新帐户共享的文件夹中。)

这是我目前所拥有的:

function copyDrive() 
  var originFolder = DriveApp.getFolderById(originFolderID);
  var destination = DriveApp.getFolderById(destinationID);
  copyFiles(originFolder, destination);

;

function copyFiles(passedFolder, targetFolder) 
  var fileContents = passedFolder.getFiles();
  var file;
  var fileName;

  while(fileContents.hasNext()) 
    file = fileContents.next();
    fileName = file.getName();
    file.makeCopy(fileName, targetFolder);
  
  copySubFolders(passedFolder, targetFolder);
;

function copySubFolders(passedFolder, targetFolder) 
  var folderContents = passedFolder.getFolders();
  var folder;
  var folderName;

  while(folderContents.hasNext()) 
    folder = folderContents.next();
    folderName = folder.getName();
    var subFolderCopy = targetFolder.createFolder(folderName);
    copyFiles(folder, subFolderCopy);
  
;

请原谅任何不雅;我对此很陌生。该脚本实际上运行良好并保留了文件夹结构,但在复制约 150 个文件和文件夹后会超时。我一直在研究如何使用延续令牌,并且仔细阅读了this post。我认为我停留在概念层面,因为我不确定延续标记将如何与我设置的递归函数交互。看起来我最终会得到一堆我的copySubFolders 函数,它们每个都需要自己的延续令牌。当然,它们的迭代器都使用相同的变量名,所以我真的不知道如何设置它。

有什么想法吗?很抱歉发布这样一个无助的新手问题;我希望这至少对某人来说是一个有趣的问题。

【问题讨论】:

有问题处理类似的问题。为了让您开始,您需要的关键部分是触发器。您可以有一个 5 分钟的重复触发器,它将尽可能多地处理并以某种方式记住它的位置,以便下一个触发器从它停止的地方继续。您可以使用脚本属性存储您的位置。它不是微不足道的,而是可行的 @ZigMandel 触发器是微不足道的,并不是真正必要的。我能够告诉脚本重复执行直到它完成,而且我认为对于我们的大多数用户来说它不必运行很多次。我知道存储位置的方法是将“延续标记”保存为脚本属性,但我的问题是关于如何使这些标记与我创建的递归函数一起正常工作。那部分有意义吗? 它将在 6 分钟后超时,因此如果您不使用触发器,它将失败,除非您的文件总数很少。如果问题是关于使用延续令牌的问题,请发布您尝试使用它们的代码。 触发器不会阻止它失败;他们只会定期重新启动它。我没有带有令牌的代码,因为我需要解决有关如何使用它们的问题。我包含了一个使用它们的项目的链接。当我有一个完整的调用堆栈时,我将有多个正在进行的文件夹迭代器,所有命名为 folderContents。如果我想停止脚本并能够从中断的地方继续,我必须为函数的每个实例保存一个不同的继续令牌,并弄清楚如何让该堆栈再次运行并将正确的令牌分配给每个级别.有意义吗? 你可以试试我的文件夹复制表插件。如果您有使其更强大的想法,我一直在寻找合作伙伴来帮助开发它。 【参考方案1】:

我想我已经解决了概念问题,虽然我得到了

很抱歉,发生服务器错误。请稍等,然后重试。 (第 9 行,文件“代码”)

当我尝试执行它时。

基本上,我将其设置为一次仅尝试复制一个***文件夹,并且对于每个***文件夹,它都使用我之前拥有的递归函数。它应该为第一级文件夹和根文件夹中的任何文件保存延续令牌,以便它可以在下一次执行中从它停止的地方继续。这样一来,我的递归函数堆栈中就不会涉及令牌。

function copyDrive() 


  var originFolder = DriveApp.getFolderById(originFolderID);
  var destination = DriveApp.getFolderById(destinationID);

  var scriptProperties = PropertiesService.getScriptProperties();
  var fileContinuationToken = scriptProperties.getProperty('FILE_CONTINUATION_TOKEN');
  var fileIterator = fileContinuationToken == null ?
    originFolder.getFiles() : DriveApp.continueFileIterator(fileContinuationToken);
  var folderContinuationToken = scriptProperties.getProperty('FOLDER_CONTINUATION_TOKEN');
  var folderIterator = folderContinuationToken == null ?
    originFolder.getFolders() : DriveApp.continueFolderIterator(folderContinuationToken);

  try 
    var rootFileName;
    while(fileIterator.hasNext()) 
      var rootFile = fileIterator.next();
      rootFileName = rootFile.getName();
      rootFile.makeCopy(rootFileName, destination);
      

    var folder = folderIterator.next();
    var folderName = folder.getName();
    var folderCopy = folder.makeCopy(folderName, destination);


    copyFiles(folder, folderCopy);

   catch(err) 
    Logger.log(err);
  

  if(fileIterator.hasNext()) 
    scriptProperties.setProperty('FILE_CONTINUATION_TOKEN', fileIterator.getContinuationToken());
   else 
    scriptProperties.deleteProperty('FILE_CONTINUATION_TOKEN');
  
  if(folderIterator.hasNext()) 
    scriptProperties.setProperty('FOLDER_CONTINUATION_TOKEN', folderIterator.getContinuationToken());
   else 
    scriptProperties.deleteProperty('FOLDER_CONTINUATION_TOKEN');
  

;

function copyFiles(passedFolder, targetFolder) 
  var fileContents = passedFolder.getFiles();
  var file;
  var fileName;

  while(fileContents.hasNext()) 
    file = fileContents.next();
    fileName = file.getName();
    file.makeCopy(fileName, targetFolder);
  
  copySubFolders(passedFolder, targetFolder);
;

function copySubFolders(passedFolder, targetFolder) 
  var subFolderContents = passedFolder.getFolders();
  var subFolder;
  var subFolderName;

  while(folderContents.hasNext()) 
    subFolder = subFolderContents.next();
    subFolderName = subFolder.getName();
    var subFolderCopy = targetFolder.createFolder(folderName);
    copyFiles(subFolder, subFolderCopy);
  
;

【讨论】:

我通过手动删除脚本属性解决了上述错误。但是,现在,最后为文件夹迭代器保存的延续令牌与开头从脚本属性加载的延续令牌相同。我不知道为什么会这样,我想它仍然符合最初的问题。 干得好!由于API quotas,它看起来像询问wait a bit。脚本已达到每位用户每 100 秒 1000 次查询的限制或已达到总日配额。您可以sleep 您的脚本或update short limits 每个用户。 看起来这个解决方案只存储一个文件夹迭代器。要递归遍历所有文件夹,您至少需要一个数组。有关详细信息,请参阅this answer。【参考方案2】:

我知道您希望通过一种简单的编程方式来执行此操作,但安装桌面版 Google 云端硬盘并让它们右键单击、复制、粘贴可能是最简单的方法。

想法:

    创建一个文件夹,用户可以在其中放置其云端硬盘的所有项目。 (我看到你已经这样做了。) 与他们的新帐户共享该文件夹。 (我知道你也已经这样做了。) 使用 Drive for Desktop 登录他们的新帐户。 复制 Drive for Desktop 中的文件夹并将其直接粘贴回去。所有权将转移到新帐户。

只是一个想法。

【讨论】:

我同意使用 Drive for Desktop 应该是一个简单的解决方案,但有几个人报告说安装后严重崩溃。我不知道为什么会发生这种情况,我也无法真正了解它。我不从事 IT 或其他工作;我只是一个随机的员工,认为他可以提供帮助。 这行不通,除非您不介意您的任何 google 文件(soreadsheets、docs 等)都不会被复制。 比尔,我了解你的情况。您最初的问题非常有效,关于如何避免超时。我会继续研究解决方案。 Zig,这非常适用于 Google 文件。我已经做了一段时间了。为什么你认为它不会? 感谢总经理! Zig,这确实适用于 Google 文件,正如我在原始问题中提到的那样。 酷。 Om 很惊讶这适用于电子表格等 google 文件。【参考方案3】:

您将需要存储文件夹迭代器和文件迭代器的数组,因为每个文件夹都可以有一个嵌套的文件夹数组。如果您在接受的解决方案中重复使用相同的文件夹迭代器,您将无法在更多***文件夹上恢复。

查看my answer 此处的模板,您可以使用该模板递归迭代驱动器中内置恢复功能的所有文件。

【讨论】:

以上是关于用于复制整个 Google Drive 文件结构的 Google Apps 脚本;如何避免超时?的主要内容,如果未能解决你的问题,请参考以下文章

将文件从已安装的 Google Drive 复制到本地 Google Colab 会话

使用Drive.Files.copy进行复制是PDF格式,而不是Google文档格式

如何同步两个 Google Drive 文件夹

IOS:如何使用 Google Drive sdk 库将文件上传到特定的 Google Drive 文件夹

Google网络应用/脚本,用于自动将文件上传到特定的g-drive文件夹

Google Drive api范围和文件访问(驱动器vs drive.files)