Download "private" files from Google Drive, and folders and files

INTRO

I have spent much time on methods for getting binary files up to Google Drive (private and public), but until now have not tackled the method for bringing private binary files stored on Google drive into App Inventor apps. This can be done by using a google apps script web app to convert the binary file to a base64 string, which can then be downloaded, as text, to the app, and converted back to a binary file. This relies on the files being owned by the google account running the web app. The same method, though not necessary, can be used for public files as well. I am relying on the excellent base64 extension provided by Juan Antonio, to handle the conversion at the app.



I provide three methods:

STAGE 1

A basic return of a single file from Google Drive to the app. This is the method in its simplest form to help with understanding and for development by others. The scripting in the web app here is used by all three methods to download the files.

STAGE 2

A return of all files in a single folder on Google Drive. The folder as well as the files are private. Probably the most required/usable method.

STAGE 3

A return of all files in all subfolders of a parent folder on Google Drive, recreating the directory structure on the android device. All folders and files are private. (This was partly inspired by seeing the struggles of a developer on the AI2 community attempting something similar with "public" files.

STAGE 1

You require the script url for the web app, and a file ID of a private binary file stored on Google Drive

HOW DOES IT WORK?

On pressing Button1, a progressDialog is started to keep the user entertained, then you call the web app script with the web component, supplying the file ID. The web app receives the file ID, fetches the file, and then converts it to a base64 string, following which it returns the base64 string and the filename as a stringified JSON list to App Inventor.

When the web component receives the content, the stringified JSON is converted to a list, the two elements of this list are then used to convert the base64 string back into a binary file with the provided file name, and the file is stored  in either the ASD or the root of the sdcard, depending on which Android version is in use.

Using the new when KIO4_Base641 .GotFile block, the progressDialog is closed, the file name is displayed, and the image is also displayed

BLOCKS

SCRIPT

function doGet(e) {

var myFile = DriveApp.getFileById(e.parameter.ID);

var b64String = Utilities.base64Encode(myFile.getBlob().getBytes());

return ContentService.createTextOutput(JSON.stringify([b64String,myFile.getName()]));

}

STAGE 2

You require the script url for the web app, and a folder ID of a private folder with private files stored on Google Drive

HOW DOES IT WORK?

When you press the Get Files in Folder button, and after giving read permission to the app (compiled), the following happens:

BLOCKS

SCRIPT

function doGet(e) {

//return filename and file as base64string from provided fileID

if (e.parameter.fn == 'file') {

var myFile = DriveApp.getFileById(e.parameter.fileID);

var b64String = Utilities.base64Encode(myFile.getBlob().getBytes());

return ContentService.createTextOutput(JSON.stringify([b64String,myFile.getName()]));

}

//return all filenames and fileIDs in provided folder

else if (e.parameter.fn == 'folder') {

var fileList=[];

var parentFolder = DriveApp.getFolderById(e.parameter.folderID);

    var files = parentFolder.getFiles();

while (files.hasNext()) {

var file = files.next();

fileList.push([file.getId(),file.getName(),parentFolder.getName()]);

}

return ContentService.createTextOutput(JSON.stringify(fileList));

}

}

STAGE 3

You require the script url for the web app, and a parent folder ID of a private folder with private subfolders and private files, stored on Google Drive

HOW DOES IT WORK?

I have broken this down into three sections (plus a delete method) for demonstration purposes (they can all be run together).

IMAGES (folders and files on Google Drive)

BLOCKS

SCRIPT

function doGet(e) {

//return filename and file as base64string from provided fileID

if (e.parameter.fn == 'file') {

  var myFile = DriveApp.getFileById(e.parameter.fileID);

  var b64String = Utilities.base64Encode(myFile.getBlob().getBytes());

  return ContentService.createTextOutput(JSON.stringify([b64String,myFile.getName()]));

}


//return all filenames and fileIDs in provided parent folder

else if (e.parameter.fn == 'filesInFolders') {

  var allList = [];

  var parentFolder = DriveApp.getFolderById(e.parameter.folderID);

  allList.push([parentFolder.getName()]);

  var folderList = [];

  var fileList = [];

  var folders = parentFolder.getFolders();

while (folders.hasNext()) {

var folder = folders.next();

    folderList.push(folder.getName());

      var files = folder.getFiles();

    while (files.hasNext()) {

    var file = files.next();

    fileList.push([file.getId(),file.getName(),folder.getName()]);

    }      

    }  

  allList.push(folderList);

  allList.push(fileList);

  return ContentService.createTextOutput(JSON.stringify(allList));

}

}

VIDEO (STAGE 3)

CREDITS

My thanks again to Juan, without whose work on the base64 extension none of this would be possible

RESOURCES

For a more dynamic folder/file listing, see HERE for ideas