由于Drive API配额,Services Quotas以及脚本执行时间限制6 min
,对于拆分Google云端硬盘驱动器文件操作通常至关重要。
我们可以使用PropertiesService为FolderIterator或FileIterator存储continuationToken
。
通过这种方式,我们可以停止我们的脚本,并在下一次运行时从我们停止的地方继续。
// Logs the name of every file in the User's Drive
// this is useful as the script may take more that 5 minutes (max execution time)
var userProperties = PropertiesService.getUserProperties();
var continuationToken = userProperties.getProperty('CONTINUATION_TOKEN');
var start = new Date();
var end = new Date();
var maxTime = 1000*60*4.5; // Max safe time, 4.5 mins
if (continuationToken == null) {
// firt time execution, get all files from Drive
var files = DriveApp.getFiles();
} else {
// not the first time, pick up where we left off
var files = DriveApp.continueFileIterator(continuationToken);
}
while (files.hasNext() && end.getTime() - start.getTime() <= maxTime) {
var file = files.next();
Logger.log(file.getName());
end = new Date();
}
// Save your place by setting the token in your user properties
if(files.hasNext()){
var continuationToken = files.getContinuationToken();
userProperties.setProperty('CONTINUATION_TOKEN', continuationToken);
} else {
// Delete the token
PropertiesService.getUserProperties().deleteProperty('CONTINUATION_TOKEN');
}
对于检索文件夹的树状结构并获取它的文件,我们必须使用递归函数。 Somethiong喜欢这样:
doFolders(DriveApp.getFolderById('root folder id'));
// recursive iteration
function doFolders(parentFolder) {
var childFolders = parentFolder.getFolders();
while(childFolders.hasNext()) {
var child = childFolders.next();
// do something with folder
// go subfolders
doFolders(child);
}
}
但是,在这种情况下,我不知道如何使用continuationToken
。
当我们需要抛出所有文件夹结构时,如何将ContinuationToken
与递归文件夹迭代器一起使用?
根据每个id
文件夹的parent
构建多个名称是否有意义?
答案 0 :(得分:1)
如果您尝试递归遍历文件夹并希望使用延续令牌(大文件夹可能需要使用),则需要一个可以存储多组延续令牌的数据结构。既适用于文件和文件夹,也适用于当前层次结构中的每个文件夹。
最简单的数据结构是对象数组。
这是一个解决方案,为您提供了用于创建函数的模板,该函数可以递归处理文件并存储连续令牌,以便在超时时可以恢复。
只需将MAX_RUNNING_TIME_MS
修改为所需的值(现在设置为1分钟)即可。
您不希望将其设置超过4.9分钟,因为脚本可能会在此之前超时并且不存储其当前状态。
processFile
方法以对文件执行任何操作。 processRootFolder()
并将其传递给Folder
。知道如何继续处理文件夹将足够聪明。当然还有改进的余地(例如,它只是检查文件夹名称以查看它是恢复还是重新启动),但这对于95%的需要连续递归遍历文件夹的人来说,就足够了。令牌。
function processRootFolder(rootFolder) {
var MAX_RUNNING_TIME_MS = 1 * 60 * 1000;
var RECURSIVE_ITERATOR_KEY = "RECURSIVE_ITERATOR_KEY";
var startTime = (new Date()).getTime();
// [{folderName: String, fileIteratorContinuationToken: String?, folderIteratorContinuationToken: String}]
var recursiveIterator = JSON.parse(PropertiesService.getDocumentProperties().getProperty(RECURSIVE_ITERATOR_KEY));
if (recursiveIterator !== null) {
// verify that it's actually for the same folder
if (rootFolder.getName() !== recursiveIterator[0].folderName) {
console.warn("Looks like this is a new folder. Clearing out the old iterator.");
recursiveIterator = null;
} else {
console.info("Resuming session.");
}
}
if (recursiveIterator === null) {
console.info("Starting new session.");
recursiveIterator = [];
recursiveIterator.push(makeIterationFromFolder(rootFolder));
}
while (recursiveIterator.length > 0) {
recursiveIterator = nextIteration(recursiveIterator, startTime);
var currTime = (new Date()).getTime();
var elapsedTimeInMS = currTime - startTime;
var timeLimitExceeded = elapsedTimeInMS >= MAX_RUNNING_TIME_MS;
if (timeLimitExceeded) {
PropertiesService.getDocumentProperties().setProperty(RECURSIVE_ITERATOR_KEY, JSON.stringify(recursiveIterator));
console.info("Stopping loop after '%d' milliseconds. Please continue running.", elapsedTimeInMS);
return;
}
}
console.info("Done running");
PropertiesService.getDocumentProperties().deleteProperty(RECURSIVE_ITERATOR_KEY);
}
// process the next file or folder
function nextIteration(recursiveIterator) {
var currentIteration = recursiveIterator[recursiveIterator.length-1];
if (currentIteration.fileIteratorContinuationToken !== null) {
var fileIterator = DriveApp.continueFileIterator(currentIteration.fileIteratorContinuationToken);
if (fileIterator.hasNext()) {
// process the next file
var path = recursiveIterator.map(function(iteration) { return iteration.folderName; }).join("/");
processFile(fileIterator.next(), path);
currentIteration.fileIteratorContinuationToken = fileIterator.getContinuationToken();
recursiveIterator[recursiveIterator.length-1] = currentIteration;
return recursiveIterator;
} else {
// done processing files
currentIteration.fileIteratorContinuationToken = null;
recursiveIterator[recursiveIterator.length-1] = currentIteration;
return recursiveIterator;
}
}
if (currentIteration.folderIteratorContinuationToken !== null) {
var folderIterator = DriveApp.continueFolderIterator(currentIteration.folderIteratorContinuationToken);
if (folderIterator.hasNext()) {
// process the next folder
var folder = folderIterator.next();
recursiveIterator[recursiveIterator.length-1].folderIteratorContinuationToken = folderIterator.getContinuationToken();
recursiveIterator.push(makeIterationFromFolder(folder));
return recursiveIterator;
} else {
// done processing subfolders
recursiveIterator.pop();
return recursiveIterator;
}
}
throw "should never get here";
}
function makeIterationFromFolder(folder) {
return {
folderName: folder.getName(),
fileIteratorContinuationToken: folder.getFiles().getContinuationToken(),
folderIteratorContinuationToken: folder.getFolders().getContinuationToken()
};
}
function processFile(file, path) {
console.log(path + "/" + file.getName());
}