Upload Any File to Google Drive, 

using Google Drive API

INTRO

Herewith, another method to upload files from your AppInventor App to Google Drive, this time using the Google Drive API, so no messing around with google apps script, or converting files to base64, ... just clean and simple file uploads (on the surface....)

SETUP

The method requires you to setup a cloud project on the Google Cloud Console, to then verify the project with your account (as test user), and then to generate access and refresh tokens that can be used in the app to authenticate with Google Drive, and allow file uploads, to a folder of your choice. Two videos have been created (please excuse the mistakes and wrong turns I made) to show the creation of a cloud project, and then the verification, along with accessing the tokens.

VIDEO Part 1 - Setting up Google Cloud Project

(Seems we only need the www.googleapis.com/auth/drive.file scope) - see also Addendum below

VIDEO Part II - Verifying Cloud Project and Getting Tokens

Commands used in Part 2 as text

curl -d "client_id=<client_id>&scope=https://www.googleapis.com/auth/drive.file" https://oauth2.googleapis.com/device/code


https://www.google.com/device


curl -d client_id=<client id> -d client_secret=<client secret> -d device_code=<device code> -d grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code https://accounts.google.com/o/oauth2/token

SETUP - cont'd

AppInventor App creation time

We now have our client_id, client_secret and refresh token. This is sufficient for the creation of blocks to send a POSTText request to Google, in order to return an access_token.

Once we have the access_token, blocks can be created to upload the file. But there is a catch. The web component cannot use multipart, therefore we have to send two web calls, the first (POSTFile) to upload the actual file (which is untitled and goes to the root folder of Google Drive), the second (PATCHtext) to send the metadata (filename, folderId) which correctly names the file, and moves it to the designated folder.

It took a fair bit of head scratching and trial and error to figure out how to do this, the issues were eventually solved (light bulb moment) when I threw some parameters at the API Explorer, which returns the code, including the correct headers, needed for cURL and HTTP.

The blocks shown below are setup to ensure that we only call for a refresh_token when one is needed, a Get Access Token button is included to show the access_token being returned.

In the "simple" demo, a file is available in the assets for testing uploads. You can upload the same file as many times as you like, it does NOT overwrite.

The workflow of the blocks goes something like this:

BLOCKS

IN CLOSING....

I have only scratched the surface here of what is possible using the Google Drive API, I guess listing a folder, and downloading files is next on the list....

This tutorial has the cloud project setup in Testing mode, with a single test user. A developer could move such a project into Production, get the project verified, then any user could connect to the project to upload files to their own google drive.

ADDENDUM

On using the above, and also by seeking out solutions for listing and downloading files, I learnt much more about how this all works.

If when setting up an 0Auth Client ID with "TV" as the device, you are severely restricted on which scopes you can use. The "drive.file" scope certainly allows the app to upload files, but you can only list and download the files that you upload. This sandbox is for the most part a good thing, because it does not expose the entirety of your google drive, however some may find this restricting, and will want to be able to offer other folders and files on the google drive for the app to access.

After doing some digging around, I found this, which explains a similar approach, but uses  "Web App" instead of "TV", and requires you to have a domain named web server ( you just have to have one, you do not need to use it after setup). With this method, I was able to setup my AppInventor app with the "drive" scope (a much more sensitive/restricted scope, giving full access to google drive), but this should be controllable through the setup in the AppInventor app .

Below, the commands required to get the "refresh_token" needed:

In a browser:

https://accounts.google.com/o/oauth2/auth?client_id=[clientID]&redirect_uri=[websiteURL]&scope=https://www.googleapis.com/auth/drive&response_type=code&include_granted_scopes=true&access_type=offline&state=state_parameter_passthrough_value

this (for me) returned a code in the url address bar:

[websiteURL]/?state=state_parameter_passthrough_value&code=4/0AWgavdcbJnFGsaAP8EvJ63r_dXtan6vuFcFEd0pZjCLyasnbLg7yf0xZ-fUswn6ZzKiYmQ&scope=https://www.googleapis.com/auth/drive

I then took this code and ran it in the following cURL command:

curl --request POST --data "code=4/0AWgavdcbJnFGsaAP8EvJ63r_dXtan6vuFcFEd0pZjCLyasnbLg7yf0xZ-fUswn6ZzKiYmQ&client_id=[clientID]&client_secret=[clientSecret]&redirect_uri=[websiteURL]&access_type=offline&grant_type=authorization_code" https://oauth2.googleapis.com/token

which returned:

{

  "access_token": "ya29.a0AX9GBdWHHENfIULGpn-DaL2iuRzDIAI1WsjEhJRHDv9XEbP8k7hkdRHQKYhpRaZGDb52yDBPHh1l-Ezd4i-q50UU4Hqd13pS1f-uBXi59dYeBXU6WFFwFckNxOLLGCjOYEpa0ljQSARMSFQHUCsbCjwSwA8QmdhjDPoSbdp-WdA0163",

  "expires_in": 3599,

  "refresh_token": "1//03DtxVgvZltvT4_Wo8YRjJ-3Lm1yK4A1UGRGmqZ1JY97A-tNb7dfd1Y5Glqh2sj2HhYnqdsDUI",

  "scope": "https://www.googleapis.com/auth/drive",

  "token_type": "Bearer"

}


It maybe that all your app needs is the "drive.file" scope, or perhaps "drive.readonly" (for listing and downloads only)...

You can request more than one scope, just put each one in a list, separated by a space, e.g.

scopes=[https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive.readonly];


Using the drive or drive.readonly scope, here are some blocks to download a file or to list files in a specific folder, using the Google Drive API with AppInventor:

Download A File

List Files in Folder