I'm currently writing a script that copies a folder, and all of its sub-folders and files of a very large directory from one location in Google Drive to another. This is being used for archiving purposes for the company which I am employed by.
My issue is that the size and quantity of the file that I am trying to archive is WAY too large to handle in the 5 minute execution time given by Google. I am trying to keep this script as a standalone web app, however I am happy to extend on it as needed.
My second issue is that I need the script to run over and over again until the folders have been copied but once it is finished I need it to stop. I was originally going to use triggers, however time-based triggers are inappropriate for the task I am trying to fulfil.
In short, I need my script to run until the task is completed, automatically restarting itself, and avoiding execution time errors.
The full code is included below.
//Global Variables
var classroomFolderName = "Classroom";
var archiveFolderName = "Archive";
function doGet(){
var classroomFolder = DriveApp.getFoldersByName(classroomFolderName).next();
var archiveFolder = DriveApp.getFoldersByName(archiveFolderName).next();
start(archiveFolder);
}
function getFolder(folderName){
var foldercount = 0;
//Selects all folders named exactally as parameter is given
var folder = DriveApp.getFoldersByName(folderName);
while (folder.hasNext()) {
var folders = folder.next();
foldercount++;
}
//Throws errors if number of folders != 1
if (foldercount < 1){
throw 1;
}
else if (foldercount > 1){
throw 2;
}
else{
return folder;
}
}
function start(archiveFolder) {
var sourceFolder = classroomFolderName;
var targetFolder = "New Archive";
var source = DriveApp.getFoldersByName(sourceFolder);
var target = archiveFolder.createFolder(targetFolder);
if (source.hasNext()) {
copyFolder(source.next(), target);
}
}
function copyFolder(source, target) {
var folders = source.getFolders();
var files = source.getFiles();
while(files.hasNext()) {
var file = files.next();
file.makeCopy(file.getName(), target);
}
while(folders.hasNext()) {
var subFolder = folders.next();
var folderName = subFolder.getName();
var targetFolder = target.createFolder(folderName);
copyFolder(subFolder, targetFolder);
}
}
This is something I came across- you may find some value in it-
https://ctrlq.org/code/20016-maximum-execution-time-limit
Related
Need help on simplifying this script. I still need to add more clusters in while loops.
function Files() {
var ss = SpreadsheetApp.getActive();
var names = ss.getSheetByName("AME");
var SupplierName = names.getRange(names.getLastRow(),5).getValue();
var Cluster = names.getRange(names.getLastRow(),3).getValue();
if (Cluster == 'US'){
var ClusterID = DriveApp.getFolderById("1z2R");
var newFolderID = ClusterID.createFolder(SupplierName);
var sourceFolder = DriveApp.getFoldersByName("US").next();
var files = sourceFolder.getFiles();
var destFolder = DriveApp.getFoldersByName(SupplierName).next();
while(files.hasNext()){
var file = files.next();
file.moveTo(destFolder);
}
return newFolderID.getId();
SUGGESTION:
We are still kind of lurking in the dark here but here are some opportunities I have identified on the provided script above.
/** Can be a global variable */
var ss = SpreadsheetApp.getActive();
var names = ss.getSheetByName("AME");
var SupplierName = names.getRange(names.getLastRow(),5).getValue();
var Cluster = names.getRange(names.getLastRow(),3).getValue();
function Files() {
if(Cluster == 'US') {
var ClusterID = DriveApp.getFolderById("SET FOLDER ID");
var folders = ClusterID.getFoldersByName(SupplierName).hasNext();
if(folders !== true){
ClusterID.createFolder(SupplierName);
moveFolders(SupplierName);
}
else {
moveFolders(SupplierName);
}
}
else {
Logger.log('Cluster is ' + Cluster);
}
}
function moveFolders() {
var sourceFolder = DriveApp.getFoldersByName("US").next();
var files = sourceFolder.getFiles();
Logger.log(files);
var destFolder = DriveApp.getFoldersByName(SupplierName).next();
while(files.hasNext()){
var file = files.next();
file.moveTo(destFolder);
}
}
Created a separate function moveFolders() to move files to the created folder ClusterID.createFolder(SupplierName); if Cluster 'US' was checked on the last row of the sheet. I assumed that your data is from a response that came from a Google Form that is why the last row is only being checked.
Sample Data:
Source parent folder for Cluster Folder and US Folder:
Files inside the "US" Folder:
After the script is run, created the SupplierName folder containing the files moved from the US Folder:
Now I haven't worked on dynamically adjusting the script to manually define the cluster name as of the moment due to lack of clarity of the question, but I'll modify my answer once questions are addressed.
I want to rename my all Audio files in particular folder in my drive by using google script.
How to get original name of file? And replace with new name that will erase first 23 words and remain all name as it was. I didn't understand how to do that.
I try following script.
function non_native_file_name_changer(folderID,fileName,fileType,iterator) {
var folder = DriveApp.getFolderById('ID');
var files = folder.getFilesByType(fileType);
var count = 1
while(files.hasNext()){
var file = files.next()
if(iterator === true){
file.setName(file.getName().slice(-23));
}else{
file.setName(fileName);
};
};
};
function start(){
var folder_ID = "ID";
var file_name = "Audio";
var file_type = "audio/amr";
var have_a_count = true;
var go = non_native_file_name_changer(folder_ID,file_name,file_type,have_a_count);
};
This not work...
Please help.
To call all files from folder but no run.
Please help.
You say you want the files from a specific folder but you didn't open a specific folder. What happens if you use
var id = "xxxxxxxxxxxxxx";
var files = DriveApp.getFolderById(id).getFiles();
while (files.hasNext()) {
var file = files.next();
Logger.log(file.getName());
}
I need to list all files and folders of google drive up to some level. The code below is listing all the files and their folders (until it exceeds its time limit) and logs them. How to I add some way to stop if it recursively went, let´s say, 3 levels of subfolders?
function listFolders(folder) {
folder = folder || DriveApp.getRootFolder();
var folderName = folder.getName();
var files = folder.getFiles();
while (files.hasNext()) {
var fileName = files.next().getName();
Logger.log(folderName + " :: " + fileName);
}
var subfolders = folder.getFolders();
while (subfolders.hasNext()) {
listFolders(subfolders.next());
}
}
How about this modification? I think that there are several answers for your situation. So please think of this as just one of them.
Modified script:
When you use this modified script, please run main().
function main() { // Added
const folder = // Please set here.
const n = 3; // Please set here. This sample sets 3 as your question.
listFolders(folder, n);
}
function listFolders(folder, n) { // Modified
folder = folder || DriveApp.getRootFolder();
var folderName = folder.getName();
var files = folder.getFiles();
while (files.hasNext()) {
var fileName = files.next().getName();
Logger.log(folderName + " :: " + fileName);
}
if (--n == 0) return; // Added
var subfolders = folder.getFolders();
while (subfolders.hasNext()) {
listFolders(subfolders.next(), n); // Modified
}
}
Note:
Your script is Google Apps Script. So I modified your script as Google Apps Script.
Please modify it for your situation.
If I misunderstood your question, please tell me. I would like to modify it.
I have a javascript I found but it starts with having the user choose a folder where it will grab files. I want to create a watch folder so I want to tell the javascript the folder to grab the files from, not let the user choose. I can't for the life of me figure out how to do this. I know applescript but cannot grasp javascript. Thank you!
Here is what I believe the area I need to change:
function main() {
// user settings
var prefs = new Object();
prefs.sourceFolder = '/Volumes/SERVER_RAID/•Current/MPC'; // default browse location (default: '~')
prefs.removeFileExtensions = true; // remove filename extensions for imported layers (default: true)
prefs.savePrompt = true; // display save prompt after import is complete (default: false)
prefs.closeAfterSave = true; // close import document after saving (default: false)
// prompt for source folder
var sourceFolder = Folder.selectDialog('Where are the Front and Back files?', Folder(prefs.sourceFolder));
// ensure the source folder is valid
if (!sourceFolder) {
return;
}
else if (!sourceFolder.exists) {
alert('Source folder not found.', 'Script Stopped', true);
return;
}
// add source folder to user settings
prefs.sourceFolder = sourceFolder;
// get a list of files
var fileArray = getFiles(prefs.sourceFolder);
// if files were found, proceed with import
if (fileArray.length) {
importFolderAsLayers(fileArray, prefs);
}
// otherwise, diplay message
else {
alert("The selected folder doesn't contain any recognized images.", 'No Files Found', false);
}
}
///////////////////////////////////////////////////////////////////////////////
// getFiles - get all files within the specified source
///////////////////////////////////////////////////////////////////////////////
function getFiles(sourceFolder) {
// declare local variables
var fileArray = new Array();
var extRE = /\.(?:png)$/i;
// get all files in source folder
var docs = sourceFolder.getFiles();
var len = docs.length;
for (var i = 0; i < len; i++) {
var doc = docs[i];
// only match files (not folders)
if (doc instanceof File) {
// store all recognized files into an array
var docName = doc.name;
if (docName.match(extRE)) {
fileArray.push(doc);
}
}
}
// return file array
return fileArray;
}
///////////////////////////////////////////////////////////////////////////////
To achieve this you'll need to specify the path to your desired folder as String.
If you take a look at the updated code sample below you'll notice that line 4 of your original code which read:
prefs.sourceFolder = '/Volumes/SERVER_RAID/•Current/MPC';
has been changed to:
prefs.sourceFolder = Folder('~/Desktop/targetFolder');
This now assumes the target folder is named targetFolder and it resides in your Desktop folder. You'll need to change the '~/Desktop/targetFolder' part as necessary to point to the folder you actually want. It also assumes you're running this on macOS as the shortcut to the Desktop folder (i.e. the ~/ part) will not be recognized on Windows.
What are the other ways I can specify the path?
You can specify path name using absolute paths such as:
prefs.sourceFolder = Folder('/Users/JohnDoe/Desktop/targetFolder');
Note: This example actually points to the same folder as the first example. Assuming that the user is called John Doe of course!
More info about setting file paths can be found here.
What other changes were made to your code example:
Lines 9 to 22 in your example code which read:
// prompt for source folder
var sourceFolder = Folder.selectDialog('Where are the Front and Back files?', Folder(prefs.sourceFolder));
// ensure the source folder is valid
if (!sourceFolder) {
return;
}
else if (!sourceFolder.exists) {
alert('Source folder not found.', 'Script Stopped', true);
return;
}
// add source folder to user settings
prefs.sourceFolder = sourceFolder;
are now redundant. Instead, they have been replaced with the following snippet which will alert you if the folder you specified cannot be found:
// ensure the source folder exists.
if (!prefs.sourceFolder.exists) {
alert('Source folder not found.\n' + prefs.sourceFolder, 'Script Stopped', true);
return;
}
I added main() on line 30 below to invoke the main function. However, if you have that elsewhere in your code you can delete it.
Updated code sample:
function main() {
// User settings
var prefs = new Object();
prefs.sourceFolder = Folder('~/Desktop/targetFolder');
prefs.removeFileExtensions = true;
prefs.savePrompt = true;
prefs.closeAfterSave = true;
// ensure the source folder exists.
if (!prefs.sourceFolder.exists) {
alert('Source folder not found.\n' + prefs.sourceFolder, 'Script Stopped', true);
return;
}
// Get a list of files
var fileArray = getFiles(prefs.sourceFolder);
// If files were found, proceed with your tasks.
if (fileArray.length) {
alert('I found image(s) in the specified folder\n' +
'Now you need to write code to perform out a task :)');
// <-- Continiue your code here
}
// otherwise, diplay message
else {
alert('The selected folder doesn\'t contain any recognized images.', 'No Files Found', false);
}
}
main(); // <-- Invokes the `main` function
/**
* getFiles - get all files within the specified source
* #param {String} sourceFolder - the path to the source folder.
*/
function getFiles(sourceFolder) {
// declare local variables
var fileArray = new Array();
var extRE = /\.(?:png)$/i;
// get all files in source folder
var docs = sourceFolder.getFiles();
var len = docs.length;
for (var i = 0; i < len; i++) {
var doc = docs[i];
// only match files (not folders)
if (doc instanceof File) {
// store all recognized files into an array
var docName = doc.name;
if (docName.match(extRE)) {
fileArray.push(doc);
}
}
}
// return file array
return fileArray;
}
I'm having trouble creating an app script that can read a file name and from that file name move the file from myDrive to a designated folder. Ultimately I want to create an app that reads a naming convention of PR_P50_MP26_OtherContent.pdf and puts that file into the MP26 folder which is in the PR folder which is in the P50 Folder. When I run the script it sends my file to the wrong folder.
Goal: to move "PR_P50_MP286_LineLowering_2006_463.pdf" file into the PR Folder which is under the P50 Folder.
What happens when I run the code is it goes to the test folder in the else statement.
function MoveFiles(){
var files = DriveApp.getRootFolder().getFiles();
while (files.hasNext()) {
var file = files.next();
var packagedFile = file.getName();
if (packagedFile.indexOf("PR_P50"))
{
var destination = DriveApp.getFolderById("PR folder of P50 Folder");
destination.addFile(file);
var pull = DriveApp.getRootFolder();
pull.removeFile(file);
}
else{
var destination = DriveApp.getFolderById("My test Folder");
destination.addFile(file);
var pull = DriveApp.getRootFolder();
pull.removeFile(file);
}
}
}
There are quite a few issues with the code but the one error you mentioned can be fixed by replacing
var packagedFile = file.getname()
with
var packagedFile = file.getName()
You are using the getFolderbyId Which is looking for the folders given ID. Try getting the ID of the folder which should be part of the url. You should also declare your folder variable outside of the while loop.
https://drive.google.com/drive/folders/FOLDERIDHERE
This will atleast be able to get the file in the correct folder. If you wish to do it by folder name you will have to call on getFolderByName("PR folder of p50 folder") which will return a fileIterator so you will have to loop through it, but if you know there is only 1 folder with that name you will be able to set folder as var folder = folders.next(). Either way you choose to do it, you should now be able to get the file into your desired folder.
function MoveFiles(){
var files = DriveApp.getRootFolder().getFiles();
var 1stdestination = DriveApp.getFolderById("FOLDERIDHERE");
var 2nddestination = DriveApp.getFolderById("FOLDERIDHERE");
while (files.hasNext()) {
var file = files.next();
var packagedFile = file.getName();
if (packagedFile.indexOf("PR_P50"))
{
destination.addFile(file);
var pull = DriveApp.getRootFolder();
pull.removeFile(file);
}
else{
destination.addFile(file);
var pull = DriveApp.getRootFolder();
pull.removeFile(file);
}
}
}
I did something similar here
var JobFolder = DriveApp.getFolderById('MYFOLDERIDHERE');
//Other stuff here
if(!results.isBeforeFirst() ){
throw new Error('Job does not exist');
} else {
if(jobId in JobFolder.getFilesByName(jobId) )
{
var ss = SpreadsheetApp.open(jobId);
var url = ss.getUrl();
return {url:url, jobId:jobId};
} else
{
var ss = SpreadsheetApp.create(jobId);
ss.insertSheet('Job Materials');
ss.insertSheet('Job Operations');
ss.insertSheet('Job Notes');
ss.insertSheet('Employees Clocked On');
var temp = DriveApp.getFileById(ss.getId());
JobFolder.addFile(temp);
var url = ss.getUrl();
return {url:url, jobId:jobId};
}
}