Download Multiple files from Multiple Folders on Google Drive

This was asked on the forum. Not too different from the filebyfile approach but some modifications are needed to the google apps script and the blocks to make this work. Because of the way google apps script iterates over the folders, you lose the folder structure, but you can at least save the files within each folder to the correct place.

To start we can prepare our filers and folder on google drive.

As you can see I have created a nested folder structure, each containing two different files (example png files). The folders and files must be at least shared with "Anyone with the link" in order to download to AI2. (it is possible to download private files, but that would require a google web app to overcome authentication)

Then write the google apps script (bound to a spreadsheet) to collect folder and file information. I have used the top folder ID as the starting point, which overcomes the chance of two folders with the same name

SCRIPT

function listFolderContents() {
  var ss = SpreadsheetApp.getActive();
  var sheet = ss.getSheetByName('Sheet1');
  sheet.clearContents();
  sheet.appendRow(["Foldername","FolderID","Filename","FileID"]);
  
  var topFolderID = DriveApp.getFolderById('1YQqIvWKccLDopsKg0o4qxFhL1oVr7xxQ');
  var foldername = topFolderID.getName();
  
  var folders = DriveApp.getFoldersByName(foldername)
  var folder = folders.next();                  
  traverseFolder(folder, sheet);
  
  sortByFoldername();
};

function traverseFolder(folder, sheet) { 
  listFilesInFolder(folder, sheet);  
  var subFolders = folder.getFolders();
  while (subFolders.hasNext()) {
    traverseFolder(subFolders.next(), sheet);
  }
}

function listFilesInFolder(folder, sheet) {
  var folderID = folder.getId();
  var foldername = folder.getName();
  var contents = folder.getFiles();  
  while(contents.hasNext()) {
    var file = contents.next();
    var filename = file.getName();
    var fileID = file.getId();
    sheet.appendRow( [foldername,folderID,filename,fileID] );     
  } 
}

function sortByFoldername(){
  var ss = SpreadsheetApp.getActive();
  var sheet = ss.getSheetByName('Sheet1');
  var range = sheet.getRange("A2:D");
  range.sort([{column: 1, ascending: true}]);
  }

I have added a sort routine to organise the folders alphabetically. Without sorting, the top folder will be first in the list, but then google decides in what order to iterate over the other folders....

Now run the scripts and generate the listing

The scripts will iterate over the top folder and all/any sub folders, no matter the level of nesting. It will produce a "flat listing" of all the files - you lose the folder structure...

SHEET

The work now moves to the AI2 app. You need to download the sheet listing as a csv and convert this to an AI2 list, which you can then work with to download the files and assign them to their respective folders. The difference to the filebyfile example is the naming of the filenames: we add the path which includes the folder. Minor adjustments and improvements elsewhere. We must thank Taifun for the "logic" in handling the asynchronous web component :)

BLOCKS

FOLDER PATHS / STRUCTURE

It is also possible to keep the folder structure together, although the apps script to list the paths is very slow, and could easily time out with long lists of files and folders. However for completeness I will list the scripts i used to generate the spreadsheet. The script that gets the path will list all the folders back to the root, therefore it may be necessary to strip some folders out of the sheet listing. There is a function for that.

SHEET

SCRIPTS

function generateFolderTree() {
  var ss = SpreadsheetApp.getActive();
  var sheet = ss.getActiveSheet();
  sheet.clearContents();
  sheet.appendRow(["Filename","FileID"]);
    var parentFolder = DriveApp.getFoldersByName("DL").next();
    getChildFolders(parentFolder);
    sortByFolderName();
    removeExtraFolders();
}

function getChildFolders(parent) {
  var ss = SpreadsheetApp.getActive();
  var sheet = ss.getActiveSheet();
  var childFolders = parent.getFolders();
  while (childFolders.hasNext()) {
  var childFolder = childFolders.next();
    var files = childFolder.getFiles();
    while (files.hasNext()) {
    var file = files.next();
      // Print list of files inside the folder
     // Logger.log(getDrivePathFromFolder(childFolder) + file.getName() + "," + file.getId());
      //var path = getDrivePathFromFolderAlt(childFolder);
      var filename = getDrivePathFromFolder(childFolder) + file.getName();
      var fileID = file.getId();
      sheet.appendRow([filename,fileID]);  
  }
    // Recursive call for any sub-folders
    getChildFolders(childFolder);
  }
}


function getDrivePathFromFolder(folder,optPath) {
  var path = optPath || '/';
  if (!folder) return '';
    if (folder.getId() === DriveApp.getRootFolder().getId()) {
    return path;
  }
  else {
    return getDrivePathFromFolder(folder.getParents().next() , '/' + folder.getName() + path);
  }
}

function sortByFolderName(){
  var ss = SpreadsheetApp.getActive();
  var sheet = ss.getSheetByName('Sheet1');
  var range = sheet.getRange("A2:B");
  range.sort([{column: 1, ascending: true}]);
  }

function removeExtraFolders() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('Sheet1');
var lastRow = sheet.getLastRow();
var rng = sheet.getRange("A2:A" + lastRow).getValues();
for (var i=0;i<rng.length;i++) {
rng[i][0] = (rng[i][0].toString().replace("<any folders above DL0 e.g. "/public/DL/">",""));
}
Logger.log(rng);
var newrng = sheet.getRange("A2:A" + lastRow);
newrng.setValues(rng);
}

See also another script from forum user wikki (thank you wikki)

File Listing Script