Friday, August 15, 2014

Writing Google Drive upload application

This day has to come. I came back from holidays and have few thousands photos. Many of them to throw away but most of them to keep, share and print.
The photos were took by a standalone camera and backed up on a hard drive.
The tries of using Google Photos Backup for Mac OS, plus.google.com, drive.google.com were for me dissapointing. The first was stalled after few dozen of photos and no restart helped. The other ones also crashed and were rather slow.

After creating an app I was able to upload without significant problems over 2 thousand photos and counting.
The app works as follow:

  • Authorize
  • Find a directory on Google Drive, if not exist then create one.
  • Get a list of all files from a destination directory.
  • Scan a local directory to find files and for each:
    • check if a name matches a pattern
    • check if not already on Google Drive
    • upload

Authentication & authorization

Authentication identifies your application and informs Google that you are you. To create simple authentication and authorization code follow: Google Drive API - Quickstart for Go
This bootstrap our efforts so we have working program which authenticats and authorizes itself with a help of user.

Caution! The quickstart example (I guess some dependencies) requires Go version 1.3. It doesn't work with 1.2 and earlier (default version in Ubuntu 14.04 package repo as August 2014).

Authorization gives your app a credential to act as a specific Google user. Google is recommending using OAuth2.0. The data you want to access is covered by a scope. In a case of Google Drive and accessing/modifying content it's 'https://www.googleapis.com/auth/drive'.

This is already done by a quickstart app from the above tutorial. The only think we want to modify is to cache an access token so we don't have to ask user for Drive permission every time.

Caching the user's access token

The OAuth2 library we are using already have a support for saving a token. There is a interface Cache and a simple implementation CacheFile:
https://godoc.org/code.google.com/p/goauth2/oauth#Cache

First, we will separate code responsible for a creating token and transport.
func GetNewToken() (*oauth.Token, *oauth.Transport) {
 // Generate a URL to visit for authorization.
 authUrl := config.AuthCodeURL("state")
 log.Printf("Go to the following link in your browser: %v\n", authUrl)
 // Read the code, and exchange it for a token.
 log.Printf("Enter verification code: ")
 var code string
 fmt.Scanln(&code)

 t := &oauth.Transport{
  Config:    config,
  Transport: http.DefaultTransport,
 }
 token, err := t.Exchange(code)
 if err != nil {
  log.Fatalf("An error occurred exchanging the code: %v\n", err)
 }
 return token, t
}
The example which tries to load a token from file and if cannot then request a new one may looks as follow:
var cache oauth.Cache = oauth.CacheFile("access_token.json")
token, err := cache.Token()
var t *oauth.Transport
if err != nil {
 log.Printf("Need a new token. Cannot load old one.")
 token, t = GetNewToken()
 cache.PutToken(token)
} else {
 t = &oauth.Transport{
  Config:    config,
  Token:     token,
  Transport: http.DefaultTransport,
 }
}
Full source code: gist.github.com/orian/96b5140b66363f4dee65

No comments:

Post a Comment