Google Script DriveApp assistance - google-apps-script

I've been using this script that helps me find manage folders on Google drive, by showing empty folders and deleting them.
I'm trying to amend it to specify the starting folder, by ID, using DriveApp.getFolderById('folderID') but I keep getting errors.
This is a section of the original code:
function getEmptyFolders() {
// Get sheet
var sheet = SpreadsheetApp.getActive().getSheetByName('Manage Empty Folders')
if (!sheet) {
SpreadsheetApp.getActive().insertSheet('Manage Empty Folders')
}
var sheet = SpreadsheetApp.getActive().getSheetByName('Manage Empty Folders')
var clearContent = sheet.clear()
var folders = DriveApp.getFolders();
var fileArray = [
["Folder Name", "Folder Id", "Folder URL"]
]
while (folders.hasNext()) {
var folder = folders.next()
// If the folder has no files or child folder in it, call them out.
if (folder.getFiles().hasNext() == false && folder.getFolders().hasNext() == false) {
var name = folder.getName()
var id = folder.getId()
var url = folder.getUrl()
// Push empty folder details into array
fileArray.push([name, id, url])
}
}
// formatting
var headerRow1 = sheet.getRange("A1:C1").setBackground("#d9d9d9").setFontColor("#000000").setFontWeight("Bold")
sheet.setColumnWidths(1, 4, 300)
// write results to the sheet
sheet.getRange(1, 1, fileArray.length, 3).setValues(fileArray)
}
which works great but when I try to change
var folders = DriveApp.getFolders();
to
var folders = DriveApp.getFolderByID('folderID');
I get an error:
TypeError: folders.hasNext is not a function
I'm quite new to Google Script and thought I'd understood the documentation, but obviously not!

Issue
That is because getFolderById(id) returns an object of type folder and as the error also suggests, folder does not have a method hasNext as you can also see in the attached link.
On the other hand, getFolders() returns an object of type FolderIterator which is actually a collection of folder objects and therefore hasNext() is required to iterate over these folders.
Although you can get the folder and iterate over all the subfolders:
var main_folder = DriveApp.getFolderByID('folderID');
var folders = main_folder.getFolders();
Solution:
If you want to use getFolderById then you should remove the while loop. Since you have a single folder there is no need of iterating and in fact it is wrong to do so:
function getEmptyFolders() {
// Get sheet
var sheet = SpreadsheetApp.getActive().getSheetByName('Manage Empty Folders')
if (!sheet) {
SpreadsheetApp.getActive().insertSheet('Manage Empty Folders')
}
var sheet = SpreadsheetApp.getActive().getSheetByName('Manage Empty Folders')
var clearContent = sheet.clear()
// get the parent folder by its id
var main_folder = DriveApp.getFolderById('folderID'); // new code
// get all subfolders
var folders = main_folder.getFolders(); // new code
var fileArray = [
["Folder Name", "Folder Id", "Folder URL"]
]
while (folders.hasNext()) {
var folder = folders.next()
// If the folder has no files or child folder in it, call them out.
if (folder.getFiles().hasNext() == false && folder.getFolders().hasNext() == false) {
var name = folder.getName()
var id = folder.getId()
var url = folder.getUrl()
// Push empty folder details into array
fileArray.push([name, id, url])
}
}
// formatting
var headerRow1 = sheet.getRange("A1:C1").setBackground("#d9d9d9").setFontColor("#000000").setFontWeight("Bold")
sheet.setColumnWidths(1, 4, 300)
// write results to the sheet
sheet.getRange(1, 1, fileArray.length, 3).setValues(fileArray)
}

Related

List Google drive folder contents to google sheets with only new files

Looking to learn how to improve my use of loops. Currently I need to list the names and URLS from a google drive Folder to a sheet and this is the code that I have:
Existing Code
function wthFolderContents() {
var folder_id = 'myFolderID';
var folders = DriveApp.getFolderById(folder_id)
var contents = folders.getFiles();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("SheetName");
sheet.clearContents()
sheet.appendRow( ['name', 'link'] );
var file;
var name;
var link;
var row;
while(contents.hasNext()) {
file = contents.next();
name = file.getName();
link = file.getUrl();
sheet.appendRow ( [name, link] );
with this code everytime the script is run the contents are cleared and then relisted. I am looking at a way of doing this dynamically / only update the new files so the script runs more effeciently.
Ive tried the following
New Code
function wthFolderContents2() {
var folder_id = '1vBzucZsb0SMOoHSWGtkUF-5QLQr5Fh1C';
var folders = DriveApp.getFolderById(folder_id)
var contents = folders.getFiles();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("WHTCert");
var lastRow = sheet.getLastRow()
var existing = sheet.getRange(1,1,lastRow,1).getValues()
for(i=1;i<lastRow;i++) {
var existingFilename = existing [i][0]
Logger.log(existingFilename)
while(contents.hasNext()) {
var file;
var name;
var link;
file = contents.next();
name = file.getName();
link = file.getUrl();
if (!name == existingFilename) {
sheet.appendRow ( [name, link] );
}
}
}
I cant get this to work, not sure what exactly where I have gone wrong. Hope someone can point me int he right direction!
Cheers
I believe your goal is as follows.
You want to reduce the process cost of your script.
Modification points:
In your script, appendRow is used. In this case, the process cost will become high. Ref
The search for files is run in a loop. In this case, the process cost will become high.
In your situation, it seems that you want to retrieve the file list just under the specific folder. In this case, I thought that when Drive API is used, the process cost can be reduced. In this answer, I would like to propose using Drive API in your script. When this is reflected in your script, it becomes as follows.
When Drive API is used, all values can be retrieved. So, I thought that your 1st process might be able to be used.
Modified script:
Before you use this script, please enable Drive API at Advanced Google services.
function wthFolderContents2() {
var folder_id = '1vBzucZsb0SMOoHSWGtkUF-5QLQr5Fh1C';
// Retrieve file list.
var q = `'${folder_id}' in parents and trashed = false and mimeType != '${MimeType.FOLDER}'`;
var fileList = [['name', 'link']];
var pageToken = "";
do {
var obj = Drive.Files.list({ q, maxResults: 1000, pageToken, fields: "nextPageToken,items(id,title)", corpora: "allDrives", supportsAllDrives: true, includeItemsFromAllDrives: true });
if (obj.items.length > 0) {
fileList = [...fileList, ...obj.items.map(({ id, title }) => [title, `https://docs.google.com/presentation/d/${id}/edit?usp=drivesdk`])];
}
pageToken = obj.nextPageToken;
} while (pageToken);
// Put the values to Spreadsheet.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("WHTCert");
sheet.clearContents();
sheet.getRange(1, 1, fileList.length, fileList[0].length).setValues(fileList);
}
When this script is run, the file list (filename and URL) is retrieved from the specific folder. And, the retrieved values to the "WHTCert" sheet.
Reference:
Files: list
Please convert this 2 script for scan folder only one subfolder and file, show folder name and link.
function wthFolderContents()
function wthFolderContents2()

Trying to create tree-view of google drive folders

Many thanks for the comments and response. That code was a little too advanced for me and I ended up finding a very inelegant solution that I used because I ran out of time. I was able to get the code to list Folder and first level of subFolders with links, but I have not yet been able to get it to iterate through all levels of folders, mostly because I just need to back up and learn a lot of the basics. I was also able to get all folders to list using some code I found to create a tree, but I couldn't get it to format in a way that you could actually see the structure, or add links. I'm going to continue to try, and will post if I sort it out. Here is what I used, which was fine for our purposes because our shared drive is fairly limited.
For reference, this was the code I used to start with:
https://superuser.com/questions/1095578/list-of-subfolder-names-and-file-links-in-google-sheets-script
function listFolders(foldername) {
var ss = SpreadsheetApp.openById(ID);
var sheet = ss.getSheetByName("sheet name");
sheet.appendRow("Parent Folder", "Name", "Link" ]);
//change the folder ID below to reflect your folder's ID (look in the
URL when you're in your folder)
var folders = DriveApp.getFolderById(ID);
var contents = folders.getFolders();
var cnt = 0;
var folderD;
while (contents.hasNext()) {
var folderD = contents.next();
cnt++;
data = [
folders.getName(),
folderD.getName(),
folderD.getUrl(),
];
sheet.appendRow(data);
};
};
Original Post:
I am a beginner using script in google sheets and I am trying to create a list of folders in a google drive with many subfolders. Ideally it would be a tree form but I'd settle for a list at this point. I don't need to list all the files, just the folders. I have been trying to get the code below to work but it keeps hanging up at calling up the spreadsheet. Can anyone help?
I have tried calling up both the folders and the spreadsheet by both name and ID but it always tells me it can't execute the getactivespreadsheet command. I have also tried to modify the code referred to in another another question but I can't get that to work either: https://ctrlq.org/code/19923-google-drive-files-list
function generateFolderIndex(myfoldername) {
var folder = DriveApp.getFolderById('0B8vOJQUb-IIVTHdudlZSVkdtdE0');
var subFolders = folder.getFolders();
var childFolders = subFolders
var ss = SpreadsheetApp.getActiveSpreadsheet('1Trv9OtJFnD4AdSHrZKFfsSu6JMV9f78H6wwZNhF2_M4');
var sheet = ss.getSheetByName('Directory');
sheet.clear(directory);
sheet.appendRow([name, link]);
while (subFolders.hasNext())
{
var childFolder = childFolders.next();
var foldername = childFolder.getname();
var name = childFolder.getName()
var link = childFolder.getUrl()
var date = childFolder.getDateCreated()
data = [name, link]
sheet.appendRow(data);
}
};
I am trying to get a sheet that lists folders and subfolders with URL links. I am currently receiving the following error message:
[19-05-31 15:32:20:911 EDT] Execution failed: Cannot find method getActiveSpreadsheet(string). (line 5, file "Code") [0.432 seconds total runtime]
Or.. the easy way...
Use DRIVE or FS DRIVE APP for desktop in PC. Usea A CMD (windows)... AND THE FUNCTION
TREE >a.txt
The generated file a.txt will display all the tree.
IT SAVES HOURS OF RESEARCH.
SpreadsheetApp.getActiveSpreadsheet() doesn't have any parameters.
However
SpreadsheetApp.openById('ssid') does require and id. I think perhaps you meant to be using openById();
openById
getActiveSpreadsheet
This is a script that I'm currently working on but it generates a list of Spreadsheets and you can exclude folders by id and files by id.
function getAllSpreadsheets() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('FilesAndFolders');
if(sh.getLastRow()>0) {
sh.getRange(1,1,sh.getLastRow(),2).clear().clearDataValidations();
}
getFnF();
SpreadsheetApp.getUi().alert('Process Complete')
}
var level=0;
function getFnF(folder) {
var folder= folder || DriveApp.getRootFolder();
//var folder=DriveApp.getRootFolder();
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('FilesAndFolders');
var files=folder.getFilesByType(MimeType.GOOGLE_SHEETS)
while(files.hasNext()) {
var file=files.next();
if(isExcluded(file.getId(),'file')){continue;}
var firg=sh.getRange(sh.getLastRow() + 1,level + 1);
firg.setValue(Utilities.formatString('=HYPERLINK("%s","%s")',file.getUrl(),'FILE: ' + file.getName()));
firg.offset(0,1).insertCheckboxes('Exclude','Include');
}
var subfolders=folder.getFolders()
while(subfolders.hasNext()) {
var subfolder=subfolders.next();
if(isExcluded(subfolder.getId(),'folder')){continue;}
var forg=sh.getRange(sh.getLastRow() + 1,level + 1);
forg.setValue(Utilities.formatString('=HYPERLINK("%s","%s")',subfolder.getUrl(),'FOLDER: ' + subfolder.getName()));
//forg.offset(0,1).insertCheckboxes('Exclude','Include');
//level++;
getFnF(subfolder);
}
//level--;
}
function isExcluded(id,type) {//type: file or folder
var type=type||'Missing Input';
var xFldrIdA=['Excluded folder ids'];
var xFileIdA=['Excluded file ids'];
var type=type.toLowerCase();
switch(type) {
case 'file':
return xFileIdA.indexOf(id)>-1;
break;
case 'folder':
return xFldrIdA.indexOf(id)>-1;
break;
default:
throw(Utilities.formatString('Error: Invalid Type: %s in isExcluded.',type));
return true;//assume excluded
break;
}
}
Your welcome to use it, perhaps it will help.

List all files and folder in google drive

I've been trying to figure this out for a while now. I hope I can get some guidance on this. The purpose of the following script is to get a full list of folders and files with subfolders and their files included.
Here is what I currently have:
var counter = 0
var files = folder.getFiles();
var subfolders = folder.getFolders();
var folderPath = folder.getName();
while (subfolders.hasNext()){
subfolder = subfolders.next();
var row = [];
//row.push(subfolder.getName(),'',subfolder.getId(),subfolder.getUrl(),subfolder.getSize(),subfolder.getDateCreated(),subfolder.getLastUpdated());
//list.push(row);
if(counter > 0){
var files = subfolder.getFiles();
}
while (files.hasNext()){
file = files.next();
var vals = file.getUrl();
var row = [];
if(counter == 0){
row.push(folder.getName(),file.getName(),file.getId(),file.getUrl(),file.getSize(),file.getDateCreated(),file.getLastUpdated())
}else{
row.push(folderPath + '/' + subfolder.getName(),file.getName(),file.getId(),file.getUrl(),file.getSize(),file.getDateCreated(),file.getLastUpdated())
}
list.push(row);
}
counter = counter + 1
}
It currently gets the folder names and file names for the current folder and it's subfolder. It doesn't go any further than that. I'm stuck trying to figure out how to get a loop going to continue until there are no more sub-folders.
It isn't a very big drive. There are less than 10 levels but would like the flexibility to go further if needed.
Recursion is beneficial in this case. The code below calls the recursive method recurseFolder() which takes a Folder and Array as a parameter. It adds all the files in the folder to a list, then calls itself on any subfolders it finds.
function test(){
var root = DriveApp.getRootFolder();
var list = [];
var list = recurseFolder(root, list);
Logger.log(JSON.stringify(list));
//This is just how I am testing the outputed list. You can do what you need.
var sheet = SpreadsheetApp.getActiveSheet();
list.forEach(function (row){
sheet.appendRow(row);
});
}
function recurseFolder(folder, list){
var files = folder.getFiles();
var subfolders = folder.getFolders();
while (files.hasNext()){ //add all the files to our list first.
var file = files.next();
var row = [];
Logger.log("File: " + folder.getName());
row.push(folder.getName(),file.getName(),file.getId(),file.getUrl(),file.getSize(),file.getDateCreated(),file.getLastUpdated())
list.push(row);
}
while (subfolders.hasNext()){ //Recurse through child folders.
subfolder = subfolders.next();
Logger.log("Folder: " + subfolder.getName());
list = recurseFolder(subfolder, list); //Past the original list in so it stays a 2D Array suitible for inserting into a range.
}
return list;
}
I'm not sure if the output is formatted how you intended so you might need to play with it a little. Note: It will easily time out if run on a larger Drive.
You need a function that will navigate the folder structure recursively, meaning that if it runs into a subfolder within a folder, it will call itself again passing that folder as a new parent.
function listFolders(parentFolderId) {
var sourceFolder = DriveApp.getFolderById(parentFolderId) || DriveApp.getRootFolder();
var folders = sourceFolder.getFolders();
var files = sourceFolder.getFiles();
while (files.hasNext()) {
var file = files.next();
//Do something
}
while (folders.hasNext()) {
var folder = folders.next();
listFolders(folder.getId());
}
}
Note that this function will still time out if you have lots of files in your Drive, in which case you need to store the state of your app using PropertiesService and schedule the function to run again using triggers via the ScriptApp. You can achieve this by saving the continuation token for your Files Iterator between script executions
More on ContinuationToken

Google Apps Script Triggers not working (List Files and Folders within Gdrive Folder)

So I created my first script by copying and pasting stuff from others. Somewhere down the line it look like I removed something that allows for triggers (both time & onchange) to work.
I tried applying some suggestions from other questions like these but without result..
function DolistFilesInFolder() {
listFilesInFolder("specified folder"); //Enter folder name in between " "
}
function listFilesInFolder(folderName) {
Logger.log(folderName);
var folders = DriveApp.getFoldersByName(folderName);
var folder = folders.next();
var contentfolders = folder.getFolders(); //fetches the folders inside the folder
var contentfiles = folder.getFiles(); //fetches the files inside the folder
// Find or add sheet with folder name
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName(folderName);
if (sheet) {
Logger.log("found");
}
else {
sheet = ss.insertSheet(folderName);
}
sheet.clear();
sheet.appendRow(["Name", "Date", "URL"]);
// Loop over folders in folder, using file iterator
while (contentfolders.hasNext()) {
var file = contentfolders.next();
var data = [
file.getName(),
file.getDateCreated(),
file.getUrl()
];
sheet.appendRow(data);
// Loop over files in folder, using file iterator
while (contentfiles.hasNext()) {
var file = contentfiles.next();
var data = [
file.getName(),
file.getDateCreated(),
file.getUrl()
];
sheet.appendRow(data)
}}
}
*For anyone interested this script lists both the Files and Folders in a Google Sheet based on the "specified folder"

unable to access folder just created Google Drive

I created a script that generates a folder in Google Drive and gets the folder ID of that folder.
Upon trying to access that folder in a unique function I get the error:
no item with the given ID could be found, or you do not have permission to access it on Folder
Please see my script below (called independently from html):
var childFolderIdA;
function doGet() {
return HtmlService.createHtmlOutputFromFile('multifile').setTitle('test – multi upload').setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function test(parent,child){
createSharedSubFolder(parent,child);
}
function createSharedSubFolder(parent,child) { // folder names as string parameters
var folders = DriveApp.getFolders();
var exist = false
while (folders.hasNext()) {
var folder = folders.next();
if(folder.getName()==parent){exist = true ; var folderId = folder.getId(); break};// find the existing parent folder
}
if(exist){ //parent folder exists
var child = DriveApp.getFolderById(folderId).createFolder(child).setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.EDIT);
var childFolderId = child.getId();
childFolderIdA = childFolderId;
}else{
var childFolder = DriveApp.createFolder(parent).createFolder(child); //create parent and child folders
childFolder.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.EDIT);
}
}
function saveFile(data,name,folderName) {
var contentType = data.substring(5,data.indexOf(';'));
var file = Utilities.newBlob(Utilities.base64Decode(data.substr(data.indexOf('base64,')+7)), contentType, name); //does the uploading of the files
DriveApp.getFolderById(childFolderIdA).createFile(file);
}
You can't depend on global variables to save state between calls. Each time you call a script a new script instance is spawned. Each one will maintain its own state.
For example:
google.script.run.createSharedSubFolder(...) --> Script Instance 1..var childFolderIdA=folderId;
google.script.run.saveFile(...) --> Script Instance 2..var childFolderIdA=null;
You can save the the folderId to the users property store:
PropertiesService.getUserProperties().setProperty("childFolderId", childFolderId);
You can retrieve the folder Id:
var folderId = PropertiesService.getUserProperties().getProperty("childFolderId");
Your code with this change:
function doGet() {
return HtmlService.createHtmlOutputFromFile('multifile').setTitle('test – multi upload').setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function test(parent,child){
createSharedSubFolder(parent,child);
}
function createSharedSubFolder(parent,child) { // folder names as string parameters
var folders = DriveApp.getFolders();
var exist = false
while (folders.hasNext()) {
var folder = folders.next();
if(folder.getName()==parent){exist = true ; var folderId = folder.getId(); break};// find the existing parent folder
}
if(exist){ //parent folder exists
var child = DriveApp.getFolderById(folderId).createFolder(child).setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.EDIT);
var childFolderId = child.getId();
PropertiesService.getUserProperties().setProperty("childFolderId", childFolderId);
}else{
var childFolder = DriveApp.createFolder(parent).createFolder(child); //create parent and child folders
childFolder.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.EDIT);
}
}
function saveFile(data,name,folderName) {
var contentType = data.substring(5,data.indexOf(';'));
var file = Utilities.newBlob(Utilities.base64Decode(data.substr(data.indexOf('base64,')+7)), contentType, name); //does the uploading of the files
var childFolderId = PropertiesService.getUserProperties().getProperty("childFolderId");
DriveApp.getFolderById(childFolderId).createFile(file);
}