Upload files in Google Drive with Golang and Google Drive API

devtud
4 min readJan 7, 2019

In this post we’ll go step by step to uploading a file in Google Drive using Golang and the Google Drive API V3.

Even if the the Google’s documentation is pretty good, I’ve encountered some difficulties in using their API, especially when using Go language.

Before starting to code, please visit https://developers.google.com/drive/api/v3/quickstart/go and enable the drive API for your Google Account (Step 1). You must choose a name for your app (eg: “MyAwesomeApp”) and the type of your app (eg: “Desktop App”). After this, you’re all set. Just download the client configuration — thecredentials.json file.

Now we can start coding! :)

For simplicity, I’ve organized all the code in a single file located at $GOPATH/src/tutorial/main.go. Besides the main.gofile, in $GOPATH/src/tutorial/ directory I have two other files:

  • image.png — the file we’ll upload to Google Drive
  • credentials.json — the credentials file downloaded after finishing Step 1 from the link above.

Our main.go file starts with importing the packages we need:

package main

import (
"context"
"encoding/json"
"fmt"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/drive/v3"
"io"
"io/ioutil"
"log"
"net/http"
"os"
)

The first functions we need are those from the Google’s quickstart from the link above (Step 3). I copied them below so you don’t need to switch the tabs ;).

// Retrieve a token, saves the token, then returns the generated client.
func getClient(config *oauth2.Config) *http.Client {
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
tokFile := "token.json"
tok, err := tokenFromFile(tokFile)
if err != nil {
tok = getTokenFromWeb(config)
saveToken(tokFile, tok)
}
return config.Client(context.Background(), tok)
}

// Request a token from the web, then returns the retrieved token.
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
fmt.Printf("Go to the following link in your browser then type the "+
"authorization code: \n%v\n", authURL)

var authCode string
if _, err := fmt.Scan(&authCode); err != nil {
log.Fatalf("Unable to read authorization code %v", err)
}

tok, err := config.Exchange(context.TODO(), authCode)
if err != nil {
log.Fatalf("Unable to retrieve token from web %v", err)
}
return tok
}

// Retrieves a token from a local file.
func tokenFromFile(file string) (*oauth2.Token, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
defer f.Close()
tok := &oauth2.Token{}
err = json.NewDecoder(f).Decode(tok)
return tok, err
}

// Saves a token to a file path.
func saveToken(path string, token *oauth2.Token) {
fmt.Printf("Saving credential file to: %s\n", path)
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Fatalf("Unable to cache oauth token: %v", err)
}
defer f.Close()
json.NewEncoder(f).Encode(token)
}

In order to operate with files on Google Drive, we need an instance of a *drive.Service. Let’s create the getService() function which returns one.

func getService() (*drive.Service, error) {
b, err := ioutil.ReadFile("credentials.json")
if err != nil {
fmt.Printf("Unable to read credentials.json file. Err: %v\n", err)
return nil, err
}

// If modifying these scopes, delete your previously saved token.json.
config, err := google.ConfigFromJSON(b, drive.DriveFileScope)

if err != nil {
return nil, err
}

client := getClient(config)

service, err := drive.New(client)

if err != nil {
fmt.Printf("Cannot create the Google Drive service: %v\n", err)
return nil, err
}

return service, err
}

Before uploading the file, let’s create a directory in which we’ll save the file. The function createDir() creates a directory under a certain parent directory and returns the newly created file. Note that for Google Drive, a directory is a file having the MimeType equal with "application/vnd.google-apps.folder".

func createDir(service *drive.Service, name string, parentId string) (*drive.File, error) {
d := &drive.File{
Name: name,
MimeType: "application/vnd.google-apps.folder",
Parents: []string{parentId},
}

file, err := service.Files.Create(d).Do()

if err != nil {
log.Println("Could not create dir: " + err.Error())
return nil, err
}

return file, nil
}

A regular file is created in a similar manner, the only difference is that it has to be filled with some content:

func createFile(service *drive.Service, name string, mimeType string, content io.Reader, parentId string) (*drive.File, error) {   f := &drive.File{
MimeType: mimeType,
Name: name,
Parents: []string{parentId},
}
file, err := service.Files.Create(f).Media(content).Do()

if err != nil {
log.Println("Could not create file: " + err.Error())
return nil, err
}

return file, nil
}

Finally, we can write our main() function. In this function we perform four steps:

  1. Open the file we want to upload to Google Drive (here we upload the image.png file from the working directory)
  2. Get the Google Drive service instance. If you run this for the first time, at this stage you will be prompted to open a link in your browser, authorize the app with a Google Drive Account, copy the obtained code into your terminal and hit ENTER. A token.json file will be created in your working path.
  3. Create a new directory called My Folder.
  4. Create a file called uploaded-image.png, store the content of the image.png file in it, and upload it.
func main() () {

// Step 1. Open the file
f, err := os.Open("image.png")

if err != nil {
panic(fmt.Sprintf("cannot open file: %v", err))
}

defer f.Close()

// Step 2. Get the Google Drive service
service, err := getService()

// Step 3. Create the directory
dir, err := createDir(service, "My Folder", "root")

if err != nil {
panic(fmt.Sprintf("Could not create dir: %v\n", err))
}

// Step 4. Create the file and upload its content
file, err := createFile(service, "uploaded-image.png", "image/png", f, dir.Id)

if err != nil {
panic(fmt.Sprintf("Could not create file: %v\n", err))
}

fmt.Printf("File '%s' successfully uploaded in '%s' directory", file.Name, dir.Name)
}

If you want to dynamically detect the MimeType of your file, you can refer to this article: https://golangcode.com/get-the-content-type-of-file.

After running main.go file for the first time you will see an output like this:

Go to the following link in your browser then type the authorization code: 
https://accounts.google.com/o/oauth2/auth?...
<code copied after authenticating at the address above> Saving credential file to: token.json
File 'uploaded-image.png' successfully uploaded in 'My Folder' directory

If you’re seeing this output, congratulations! :)

Now go to Google Drive (web version) and check if My Folder directory is there.

Hey, guys! This is my first post on Medium and I hope it’s helpful. If something is not clear in the post above or if I forgot something please let me know. Cheers!

--

--