I've been using DocsList for a big project and it was working perfectly. Lately, bugs have been popping up and they mostly have roots with getting a folder or file. When I did research, I found that DriveApp had been updated. The problem is that DriveApp doesn't have search parameters like DocsList had.
For example, if I had a folder structure like this:
Root
-Main Folder 1
--Folder 1
--Folder 2
-Main Folder 2
--Folder 1
--Folder 2
To get folder "Folder 1" in "Main Folder 2," I could put in the search parameter like so: DocsList.getFolder('Main Folder 2/Folder 1')
With DriveApp, I just can't understand how to work with it. From what I understand, I have to do something like this for DriveApp:
var mainFolders = DriveApp.getFoldersByName('Main Folder 2');
while (mainFolders.hasNext()) {
var mainFolder = termFolders.next();
var subFolders = termFolder.getFoldersByName('Folder 1');
// Something like this...
}
So if I had a folder that is more "deep" I would have to expand this even further..?
I feel like instead of making things easier, they made it more complicated with all the FileIterators and FolderIterators. And just making it hard to "get" a file or folder in code terms.
So basically, the point of this thread is to find out how a person who is use to DocsList to navigate and edit Drive files/folders can migrate to DriveApp and achieve the same things.
Small/Discrete examples of different scenarios would be really helpful. I can take it from there. I'll edit this more, if you guys think I'm not being clear about what I need help on.
The discussions from wchiquito's comment are an interesting read, but following all the links is time-consuming.
Bottom line: There will not be DriveApp version of getFolderByPath(), so you will need to roll your own. In the Google+ group, Faustino proposed a work-around and Eric improved it. Here it is, with an added check to allow paths that start with "/".
function getFolderByPath(path) {
var parts = path.split("/");
if (parts[0] == '') parts.shift(); // Did path start at root, '/'?
var folder = DriveApp.getRootFolder();
for (var i = 0; i < parts.length; i++) {
var result = folder.getFoldersByName(parts[i]);
if (result.hasNext()) {
folder = result.next();
} else {
return null;
}
}
return folder;
}
With that, you can simply do myFolder = getFolderByPath('Main Folder 2/Folder 1');. You will end up with a DriveApp Folder instance.
The code below works at mine.
It is based on having the same Id
function convertFileFromDocsListToDriveApp(file)
{ // Because of difference between DocsList and DriveApp
return (file === null) ? null : DriveApp.getFileById(file.getId());
}
function convertFolderFromDocsListToDriveApp(folder)
{ // Because of difference between DocsList and DriveApp
return (folder === null) ? null : DriveApp.getFolderById(folder.getId());
}
I call this in a few 'strategic' positions in my code.
I didn't test conversion from DriveApp to DocsList, but I expect this to work as well.
Related
I have a Google Apps Script for Sheets (running as a script under Sheets) that is supposed to return all the folders in my Google drive, however, the following code is stuck in an endless loop and only shows 1 of 3 folders in the drive - over and over again.
//this doesn't work -only shows 1 folder and repeats indefinitely
function getMyFolders() {
var f;
while (DriveApp.getFolders().hasNext()) {
f = DriveApp.getFolders().next();
console.log("f: " +f.getName());
}
}
The code returns the name of only one of my folders (hasNext() is clearly not working or there is a bug in Google Apps Scripts - since the while condition is never false (I ran it for several minutes and it never stopped!)
Could this be a security issue? This is just one of the problems I've run into. The other is that the 3 folders are subfolders of the same parent folder. No sure why getFolders() is not returning just the parent (that would make more sense).
Get All My Files
function getAllMyFiles(folder=DriveApp.getRootFolder()) {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
let files = folder.getFiles();
while(files.hasNext()) {
let file = files.next()
sh.appendRow([file.getName(),file.getId()]);
}
let sfldrs = folder.getFolders();
while(sfldrs.hasNext()) {
let sfldr = sfldrs.next();
getAllMyFiles(sfldr)
}
}
Try this as your function instead to return just the parent folders!
function getMyFolders() {
let folders = DriveApp.getFolders();
while (folders.hasNext()) {
console.log("f: " +folders.next().getName());
}
}
If you want child folders as well, you would need to use recursion. Are you familiar with the concept? Happy to update my answer with that information if it's helpful.
Explanation:
You were calling DriveApp.getFolders(); on every iteration, so hasNext() was always retrieving the same item (the first folder in the iterator). Hence, you had an infinite loop.
I rewrote Cooper's answer to get all folders underneath a given parent.
In my case the parent folder is called "Gill".
function getAllMyFolders(folder=DriveApp.getFoldersByName("Gill").next()) {
let sfldrs = folder.getFolders();
while(sfldrs.hasNext()) {
let sfldr = sfldrs.next();
console.log("folder name: " +sfldr.getName());
getAllMyFolders(sfldr)
}
}
Ok - I played around with this code and discovered recursion is completely unnecessary, so here's code that will return all folders at all levels, then you can test the level with getParents()
function getMyFolders() {
let folders = DriveApp.getFolders();
while (folders.hasNext()) {
let folder = folders.next();
Logger.log(folder.getName() + ", Parent Folder: " + folder.getParents().next().getName());
}
}
I had a hunch that using a recursive function was the wrong way to go (and it looks terrible plus adds unnecessary overhead) but was confused why, my first attempt in this question did not work. The answer was the way my code was written - apparently, you need to assign DriveApp.getFolders() to a variable only once. Simply putting it in more than once, seems to reset it. That is, checking for DriveApp.getFolders().hasNext() and followed by DriveApp.getFolders().next() will cause the endless loop! Lesson here: assign it to a variable and then check for hasNext as calling it changes the state of the iterator which is reset again if getFolders is called again.
That was my actual bug here.
I'm trying to get a collection of files where user (let's use billyTheUser#gmail.com) is an editor.
I know this can be accomplished almost instantly on the front-end of google drive by doing a search for to:billyTheUser#gmail.com in the drive search bar.
I presume this is something that can be done in Google App Scripts, but maybe I'm wrong. I figured DriveApp.searchFiles would work, but I'm having trouble structuring the proper string syntax. I've looked at the Google SDK Documentation and am guessing I am doing something wrong with the usage of the in matched to the user string search? Below is the approaches I've taken, however if there's a different method to accomplishing the collection of files by user, I'd be happy to change my approach.
var files = DriveApp.searchFiles(
//I would expect this to work, but this doesn't return values
'writers in "billyTheUser#gmail.com"');
//Tried these just experimenting. None return values
'writers in "to:billyTheUser#gmail.com"');
'writers in "to:billyTheUser#gmail.com"');
'to:billyTheUser#gmail.com');
// this is just a test to confirm that some string searches successfully work
'modifiedDate > "2013-02-28" and title contains "untitled"');
Try flipping the operands within the in clause to read as:
var files = DriveApp.searchFiles('"billyTheUser#gmail.com" in writers');
Thanks #theAddonDepot! To illustrate specifically how the accepted answer is useful, I used it to assist in building a spreadsheet to help control files shared with various users. The source code for the full procedure is at the bottom of this post. It can be used directly within this this google sheet if you copy it.
The final result works rather nicely for listing out files by rows and properties in columns (i.e. last modified, security, descriptions... etc.).
The ultimate purpose is to be able to update large number of files without impacting other users. (use case scenario for sudden need to immediately revoke security... layoffs, acquisition, divorce, etc).
//code for looking up files by security
//Posted on stackoverlow here: https://stackoverflow.com/questions/62940196/return-collection-of-google-drive-files-shared-with-specific-user
//sample google File here: https://docs.google.com/spreadsheets/d/1jSl_ZxRVAIh9ULQLy-2e1FdnQpT6207JjFoDq60kj6Q/edit?usp=sharing
const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("FileList");
const clearRange = true;
//const clearRange = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("ClearRange").getValue();
//if you have the named range setup.
function runReport() {
//var theEmail= SpreadsheetApp.getActiveSpreadsheet().getRangeByName("emailFromExcel").getValue();
//or
var theEmail = 'billyTheUser#gmail.com';
findFilesByUser(theEmail);
}
function findFilesByUser(theUserEmail) {
if(clearRange){
ss.getDataRange().offset(1,0).deleteCells(SpreadsheetApp.Dimension.ROWS)
}
var someFiles = DriveApp.searchFiles('"' + theUserEmail + '" in writers');
var aListOfFiles = []
while(someFiles.hasNext()){
var aFile = someFiles.next();
aListOfFiles.push([aFile.getId()
,aFile.getName()
,aFile.getDescription()
,aFile.getSharingAccess()
,aFile.getSharingPermission()
,listEmails(aFile.getEditors())
,listEmails(aFile.getViewers())
,aFile.getMimeType().replace('application/','').replace('vnd.google-apps.','')
,aFile.getDateCreated()
,aFile.getLastUpdated()
,aFile.getSize()
,aFile.getUrl()
,aFile.getDownloadUrl()
])
}
if(aListOfFiles.length==0){
aListOfFiles.push("no files for " + theUserEmail);
}
ss.getRange(ss.getDataRange().getLastRow()+1,1, aListOfFiles.length, aListOfFiles[0].length).setValues(aListOfFiles);
}
function listEmails(thePeople){
var aList = thePeople;
for (var i = 0; i < aList.length;i++){
aList[i] = aList[i].getEmail();
}
return aList.toString();
}
I'm trying to write a script that will sort large numbers of photos and videos on Google Drive into folders that make it easier for my wife to find what she's looking for. Unfortunately, she has to use Drive rather than Google Photos, so she doesn't have access to the much more robust sorting functionality that Photos would give her.
Warning: I'm not a programmer, so I really don't know what I'm doing. I think I'm telling the script what folder to look in and then telling it to search for files that meet certain criteria in terms of name of the file, but the script is returning null from its search results.
I have tried using both searchFiles (with "name contains [my string var]" as the param), and getFilesByName (with my string var as the param). In both cases, I'm getting named folders but no files.
var currDate = strMonth + strDay;
var photos = currFolder.getFilesByName(
"" + "2019" + currDate + "");
var activeFolder = currFolder.createFolder(currDate);
if (photos.hasNext()) {
myLog = Logger.log("HasNext");
} else {
myLog = Logger.log("Doesn't HasNext");
}
while ( photos.hasNext() ) {
var currPhoto = photos.next();
currPhoto.makeCopy(activeFolder);
myLog = Logger.log(currPhoto);
}
What I expect is that photos is a list of pictures the script has found, that can then be copied over to the folder I created. My debug logging always tells me hasNext() is FALSE, though, and this seems to agree with the fact that no files are getting copied into the folder.
Any help would be greatly appreciated. I'm sure I'm just doing something dumb. Thanks!
Here's an example that will display all of your JPEG files
function findJPEGFiles() {
var files=DriveApp.getFilesByType(MimeType.JPEG);
var html='<style>td,th{border:1px solid black;}</style><table><tr><th>Name</th><th>Id</th><th>Url</th></tr>';
while(files.hasNext()) {
var file=files.next();
html+=Utilities.formatString('<tr><td>%s</td><td>%s</td><td>%s</td></tr>',file.getName(),file.getId(),file.getUrl());
}
html+='</table>';
var userInterface=HtmlService.createHtmlOutput(html);
SpreadsheetApp.getUi().showModelessDialog(userInterface, 'Files');
}
I have hundreds of Google Docs, and lots of Google Folders. They share a common string in the filename. The Docs need to go into the corresponding Folder, and I'd like to automate this with some Script.
What I want to do is as follows:
I have hundreds of Google Docs which follow a similar pattern in the filename:
SURNAME, Forename_userID_fileName
So for instance,
DOE, Jane_jd5678_Document3
PUBLIC, Tom_tp9123_reportTPS
SMITH, John_js1234_finaldocument
and so on and so forth.
I also have corresponding Folders in a Google Team Drive, set up as follows:
SURNAME, Forename (userCode) (userID) - userType
So for instance,
DOE, Jane (145374578) (jd5678) - AET Full Time
PUBLIC, Tom (673468714) (tp9123) - NR Full Time
SMITH, John (874512456) (js1234) - AET Part Time
The common string between the files and the folder names is the userID string.
Currently I download all the Docs, and then upload drag-and-drop into each corresponding folder. Takes ages! I was hoping there is a bit of Apps Script that I can do to automate this but I really don't know where to start at all. Anyone have any pointers?
This StackOverflow question seems to want to do what I want to do, but as I'm such a beginner with Apps Script I can't really decipher the solution.
This is a basic example to point you in the right direction using the basic Google Apps Script DriveApp (as opposed to the advanced Script Services version or other Google Drive API methods). The main concern that comes to mind is the script timing out if this example takes too long to complete. If that happens, you'll need a more complex solution. You could also chunk it up by having the idRegex only work on, e.g., surnames A-D then E-G, etc. The example regular expression could match random files you have with underscores; you may want to find a more complicated regular expression if that is a concern.
function organizeFilesById() {
var idRegex = /_[A-z]+[0-9]+_/;
var files = DriveApp.getFiles();
while (files.hasNext()) {
var file = files.next();
var filename = file.getName();
var id = idRegex.exec(filename);
if (! id) {
continue;
} else {
id = id[0];
}
var query = "title contains '" + id + "'";
var folders = DriveApp.searchFolders(query);
if (folders.hasNext()) {
var folder = folders.next();
Logger.log("Will add %s to %s", filename, folder.getName());
//folder.addFile(file); // uncomment line to actually do it
} else {
Logger.log("No folder found for %s", filename);
// could create folder or just report "folder not found"
}
}
}
I know this question has been asked before, but I am trying to go a bit deeper in the problem. I have this apps script code I have taken online and I am using it in a google spreadsheet, to scan for a list of folders.
In short, I have a (shared) folder structure with most likely over 1000 sub-folders, which contain sub-subfolders, that looks similar to this:
MAIN FOLDER
FOLDER 1
FILE 1.1
SUBFOLDER 1.A
SUBFILE 1.A.2
SUBFILE 1.A.3
FOLDER 2
FILE 2.1
SUBFOLDER 2.A
SUBFILE 2.A.2
SUBFILE 2.A.3
FOLDER 3
FILE 3.1
SUBFOLDER 3.A
SUBFILE 3.A.2
SUBFILE 3.A.3
...
I am trying to fix 2 things:
1) My function obviously breaks when running because there are too many folders and files. I don't think it's due to the limit of rows in a spreadsheet. So, ideally, I would have to manually batch 200 folders in the main folder, run the function, and re-iterate. Any hints how to avoid that, and how to potentially, dump temporary results in a first spreadsheet and create a new spreadsheet if it's a caching problem?
2) I am not able to retrieve subfolders details and the details of the files it contains. Can anyone help adapt the code to go deeper in the folder hierarchy? Do I need some kind of loop function here?
With the code provided hereunder which I have used and tested a few times, I can only retrieve the folders 1,2,3 and files 1.1, 2.1, 3.1...but not subfolders or subfiles.
Fixing this function, would save me weeks of manual work. Any help is welcomed. Thanks in advance for your help.
function getAndListFilesInFolder() {
var arr,f,file,folderName,subFolders,id,mainFolder,name,own,sh,thisSubFolder,url;
sh = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
sh.getRange(1, 1, 100, 10).clear({contentsOnly: true})
id = "FOLDER ID HERE";
arr = [["FILE NAME", "URLs", "OWNER","FOLDER"]];
mainFolder = DriveApp.getFolderById(id);
subFolders = mainFolder.getFolders();
folderName = mainFolder.getName();
f = mainFolder.getFiles();
while (f.hasNext()) {
file = f.next();
name = file.getName()
url = file.getUrl()
own = file.getOwner().getName()
arr.push([name, url, own, folderName]);
};
while (subFolders.hasNext()) {
thisSubFolder = subFolders.next();
f = thisSubFolder.getFiles();
folderName = thisSubFolder.getName();
while (f.hasNext()) {
file = f.next();
name = file.getName()
url = file.getUrl()
own = file.getOwner().getName()
arr.push([name, url, own,folderName]);
};
};
sh.getRange(1,1, arr.length, arr[0].length).setValues(arr);
sh.getRange(2,1, arr.length, arr[0].length).sort(1);
//var second = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2")
//second.activate()
}
The reason you aren't seeing any of the subfolders is because you are not actually adding them to your array (arr) at any time.
What you really need here is is to write a recursive function, which receives a folder and returns itself plus it's file's details details in an array. When you encounter a folder, you pass that folder back to the same function.
Investigate recursion and recursive functions.
Here is a brief example you could expand, it'll need some tweaking since I'm writing this off the top of my head:
function getFilesFromFolder(folder){
var arr = [];
arr.push([folder.getName(),folder.getUrl(),folder.getOwner().getName()])
var subfolders = folder.getFolders();
var files = folder.getFiles():
while(files.hasNext()){
arr.push(/*file details*/);
}
//recurse into subfolders here.
while(subFolders.hasNext()){
arr.concat(getFilesFromFolder(subFolders.next()); //notice how we call the function from within itself.
}
return arr;
}