The vast majority of documentation about Google Drive revolves around the use of OAuth to do client->gdrive directly, bypassing your own server. In our case, we need to be very specific about where documents are placed, how directories are named and what files are acceptable. As such, it is impossible to securely do so on a client application. We must pass the documents “through” our server.
Currently, we do this with Amazon quite a bit. It’s a seamless process and users have no idea where our storage is. However, in that situation, it is simply because we choose to store all files on Amazon S3 as a structural design. In this case, our clients utilize Google Apps for their email and Google Drive for corporate shared drives. So their applications will be saving documents produced on their external staff’s mobile applications directly onto the google drive where the office staff will have access to them on google drive.
While this can be done in a variety of ways, it is far easier to use a “Service Account” (Google Service Accounts) that will make a new account for the company BUT it is not “actually” part of the company. (A company admin will not see it among the users)
Once you get the service account, you’ll get a JSON file with a private key and the email. Save that file!.
Let’s get to the good part! The first step is to get authorized with Google.
using Google.Apis.Drive.v3; public static GoogleCredential GetSAC() { // the location of the security file from google api development service account string credentialsFile = "that-json-file-you-got-from-google.json"; string path = HttpContext.Current.Server.MapPath("~/App_Data"); string keyFilePath = path + "/" + credentialsFile; // load the file into a stream so we can send it to google var stream = new FileStream(keyFilePath, FileMode.Open, FileAccess.Read); // prepare a credential from google (not approved yet, just set up) var credential = GoogleCredential.FromStream(stream); //define exactly what kind of authority we want from google string[] scopes = new string[] { DriveService.Scope.Drive }; // Full access //send that request along with our security account in the credential. credential = credential.CreateScoped(scopes); // at this point, we have a credential object that we'll send to create our Drive Service return credential; }
Now, I popped the JSON file into the App_Data directory, but you can put it anywhere on the server. I’d SERIOUSLY recommend that you don’t keep yours there, but rather someplace more secure so you can actually put your code under version control.
So this little script loads that credentials file as a stream, the scope of permissions we want into this credentials object. Now we need to create the service. That’s where we send the credentials to Google, and ask for access to a set of APIs. The plugin will handle the addresses, etc. but you “could” just code it all using HTTP calls. In the background, all it’s doing is getting a bearer token that will be going in the header of the requests.
Anyway, here’s how we create the service that we will use to execute all the Google Drive Commands:
/* We create a service object and include our app name and the credentials we just put together (we could also initialize other services in the same way if we chose) */ public static DriveService GetService() { // Login to google GoogleCredential credential = GetSAC(); // Create Google Drive service API and attach our credential to it. // note: your ApplicationName can actually be whatever you want. I don't think it makes a difference DriveService service = new DriveService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = "yourApplicationName", }); return service; }
Okay, now we’ve got the service object. At this point, we really have done nothing but get together our credentials and an object that has a list of command functions built into it. Google hasn’t been contacted at all. So, let’s do that!
The first step is going to be über-simple. We’ll just get a directory list. If you just made the account, then it should have nothing there but the “Welcome to Google Drive!” file.
So, here’s the function that will get these files. You’ll notice I added a couple parameters: query and service.
Quite often you’ll be doing several things at once. For example, if you want to make a folder, it’s best to check to see if that folder exists already (same with files). Google is VERY different than Amazon’s S3. S3 uses the actual path as a key, but Google uses more of a relational database structure, where a file has an array of parent folders.
///Get list of all files on the Google Drive. public static List GetGoogleFiles(string query = "", DriveService service=null) { // retreive an active service with google's token of authority if one doesn't already exist if (service == null) service = GetService(); // define parameters of request. FilesResource.ListRequest FileListRequest = service.Files.List(); // search examples: /* https://developers.google.com/drive/api/v3/search-parameters modifiedTime > '2012-06-04T12:00:00' and (mimeType contains 'image/' or mimeType contains 'video/') mimeType = 'application/vnd.google-apps.folder' for example: name contains 'hello' and name contains 'goodbye' */ //listRequest.PageSize = 10; //listRequest.PageToken = 10; FileListRequest.Fields = "nextPageToken, files(id, name, size, version, createdTime, parents)"; FileListRequest.Q = query; //get file list. IList files = FileListRequest.Execute().Files; List FileList = new List(); if (files != null && files.Count > 0) { foreach (var file in files) { GoogleDriveFile File = new GoogleDriveFile { ID = file.Id, Name = file.Name, Size = file.Size, Version = file.Version, CreatedDate = file.CreatedTime, Parents = file.Parents }; FileList.Add(File); } } return FileList; }
I left some examples of parameters in there, since it isn’t nearly as intuitive as you’d think, and getting the structure wrong results in a nasty error rather than no results.
There’s a bit more information about parameters here: Google’s single page about parameters, but you’ll be better off doing it by trial and error. I was pretty disappointed with their documentation (hence this entire post).
So, here’s how this will work in your API controller:
public IHttpActionResult Get() { List g = GoogleDriveModule.GetGoogleFiles(); return Ok(g); }
That should give you all the files. Not just in that directory either…. ALL of them. So, you’ll want to get them filtered down a bit before things get out of hand.
One thing you need to know is that “directories” are just files. So, you’ll make a new directory, and it gives you an ID. Each file has a “Parents” property that contains a string array of these IDs.
The next post will have some examples of uploads, deleting, downloading, making directories, updating files, etc… For now, this should get you going.