One Eyed Astericks

Got a piece you wish to share, message us and we will get back to you with in a week.

Follow publication

--

How to iterate through Google Drive Folders and Files using Google Drive API v2 in Golang?🤔

Introduction

Google Drive API allows us to create apps that leverage Google Drive cloud storage. You can develop applications that integrate with Google Drive, and create robust functionality in your application using Google Drive API

Google Drive API Relationship Diagram: Credits Google

In the diagram shared above Drive API and OAuth being the only unknown, let’s quickly dive into what they are and how are they configured.

Google Drive API allows us to leverage Google Drive storage from within your app.

OAuth currently in version 2.0 is required by any application that uses Google Sign-in, it helps google to validate any given login along with various access rights and permission granted at the time of login.

For any API level interaction with a given google account, we first need to enable the drive API.

Enabling the Drive API

To interact with the Drive API, you need to enable the Drive API service for your app. You can do this in the Google API project for the app.

To enable the Drive API, complete these steps:

  1. Go to your Google API Console by clicking here
  2. Select a project
  3. In the sidebar on the left, expand APIs & auth and select APIs
  4. In the displayed list of available APIs, click the Drive API link and click Enable API
  5. Download credentials.json and paste it in the same working directory as main.go file.

I’m using Visual Studio Code for writing code, these are the imports we are gonna do. Though this import block is generated automatically by VS Code.

package mainimport ("encoding/json""fmt""io/ioutil""log""net/http""os""strings""golang.org/x/net/context""golang.org/x/oauth2""golang.org/x/oauth2/google""google.golang.org/api/drive/v2")

NOTE: Google Drive API package is “google.golang.org/api/drive/v2” in case VS Code fetches “google.golang.org/api/drive/v3” simply replace 3 with 2.

Let’s Begin Coding: <Hello World!>

Start by writing the main function and do everything else parallelly

func main(){
// Initializing google drive API interaction
b, err := ioutil.ReadFile("credentials.json")if err != nil {log.Fatalf("Unable to read client secret file: %v", err)}// If modifying these scopes, delete your previously saved token.json.config, err := google.ConfigFromJSON(b, drive.DriveReadonlyScope)
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
// If modifying these scopes, delete your previously saved token.json.config, err := google.ConfigFromJSON(b, drive.DriveReadonlyScope)if err != nil {log.Fatalf("Unable to parse client secret file to config: %v", err)}srv, err := drive.New(client)if err != nil {log.Fatalf("Unable to retrieve Drive client: %v", err)}}

By running this much code on the terminal you should be able to see something similar:

URL screen on terimal for adding token.Json

NOTE: “drive.DriveReadonlyScope” states read-only scope which means that all this app can do is read from google drive. There are many scope options in case you wish to do more than reading files/folders free to experiment with the various scope options.

By typing “drive.” on the VS Code you can get a list of all scope options available and what they offer. Though downloading of token.json is a one time operation it is this file that contains the scope of your application in case you change the scope don’t forget to delete the token.json file and get the frest copy with modified scope

Let’s define a function capable of fetching all child elements of any given folder in google drive. This function will take drive .Service and folder ID (string) as input. It will return a list of all files and folders inside the given folder ID. (along with an error if any)

// AllChildren fetches all the children of a given folderfunc AllChildren(d *drive.Service, folderID string) ([]*drive.ChildReference,error) {var cs []*drive.ChildReferencepageToken := ""for {q := d.Children.List(folderID)// If we have a pageToken set, apply it to the queryif pageToken != "" {q = q.PageToken(pageToken)}r, err := q.Do()if err != nil {fmt.Printf("An error occurred: %v\n", err)return cs, err}cs = append(cs, r.Items...)pageToken = r.NextPageTokenif pageToken == "" {break}}return cs, nil}

Every google drive folder has as URL in below mentioned manner:

https://drive.google.com/drive/u/0/folders/1Lv-Oh9KLDkxjqGH6xPfjkBH-4z

The content in bold after the rightmost forward falling slash is folder ID. This ID is unique for any given folder inside the entire universe of google drives.

Adding few more lines inside the main function to fetch list containing all children's and to run a loop over it

elements, err := AllChildren(srv,"1g7ZEi_3l3aTxUTDTjRGSZ3Xl_dPjSIIb")if err != nil {log.Fatalf("Unable to retrieve Zone folders: %v", err)}for _, element:= range elements{fmt.Printf("%+v\n", )fmt.Scanln()}

The code written in bold will render something similar to the following output:

{
ChildLink:https://www.googleapis.com/drive/v2/files/1jh8
Id:1jh8SxvSi6q8pVCnDnmyKHqBKbOrVgnyQ
Kind:drive#childReference
SelfLink:https://www.googleapis.com/drive/v2/files
ServerResponse:{HTTPStatusCode:0 Header:map[]} ForceSendFields:[] NullFields:[]
}

As one can see folder fetched by function AllChildren contain only theses 6 heads as information. Clearly parameters like folder(file) name, size, … are missing.

To fetch all such parameters for any given child inside the folder we will have to define a new function to get such elements of any given file/folder in google drive. This function will take drive .Service and folder ID (string) as input. It will return a filename, file sze, etc for the given ID. (along with an error if any)

// PrintFile fetches the given filefunc fileDetails(d *drive.Service, fileID string) (*drive.File, error) {f, err := d.Files.Get(fileID).Do()if err != nil {return nil, err}return f, err}

Adding few more lines inside the main function to fetch file elements

elements, err := AllChildren(srv,"1g7ZEi_3l3aTxUTDTjRGSZ3Xl_dPjSIIb")if err != nil {log.Fatalf("Unable to retrieve Zone folders: %v", err)}for _, element:= range elements{elementFeilds, err := fileDetails(srv, element.Id)if err != nil {log.Fatalf("Unable to retrieve Drive folder's name: %v", err)}fmt.Printf("%+v\n", elementFeilds)fmt.Scanln()}

The code written in bold will render something similar to the following output:

&{
AlternateLink:https://drive.google.com/drive/folders/1jh AppDataContents:false
CanComment:false
CanReadRevisions:false
Capabilities:0xc0002a6190
CopyRequiresWriterPermission:false
Copyable:false
CreatedDate:2021-02-06T04:31:35.573Z
DefaultOpenWithLink:
Description:
DownloadUrl:
DriveId:
Editable:true
EmbedLink:https://drive.google.com/embeddedfolderview
Etag:"MTYxMjU4Njk5MjI3NQ"
ExplicitlyTrashed:false
ExportLinks:map[]
FileExtension:
FileSize:
0
FolderColorRgb:
FullFileExtension:
HasAugmentedPermissions:
false
HasThumbnail:false
HeadRevisionId:
IconLink:
https://drive-thirdparty.googleusercontent.com/1 Id:1jh8SxvSi6q8pVCnDnmyKHqBK
ImageMediaMetadata:<nil>
IndexableText:<nil>
IsAppAuthorized:false
Kind:drive#file
Labels:0xc0000
LastModifyingUser:0xc0002a
LastModifyingUserName:Binj
LastViewedByMeDate:
MarkedViewedByMeDate:
1970-01-01T00:00:00.000Z
Md5Checksum:
MimeType:
application/vnd.google-apps.folder
ModifiedByMeDate:
ModifiedDate:
2021-02-06T04:49:52.275Z
OpenWithLinks:map[]
OriginalFilename:
OwnedByMe:
false
OwnerNames:[Binj]
Owners:[0xc0002a]
Parents:[0xc0002ac]
PermissionIds:[]
Permissions:[]
Properties:[]
QuotaBytesUsed:0 SelfLink:https://www.googleapis.com/drive/v2/files/1jh8SxvSi6q8p Shareable:false
Shared:true
SharedWithMeDate:
SharingUser:
<nil>
Spaces:[drive]
TeamDriveId:
Thumbnail:
<nil>
ThumbnailLink:
ThumbnailVersion:
0
Title:District
TrashedDate:
TrashingUser:
<nil>
UserPermission:0xc0002c
Version:14
VideoMediaMetadata:<nil>
WebContentLink:
WebViewLink:
WritersCanShare:
true
ServerResponse:{HTTPStatusCode:200
Header:map[Alt-Svc:[h3-29=":443";.....]
Cache-Control:[no-cache, no-store, max-age=0, must-revalidate] Content-Security-Policy:[frame-ancestors 'self']
Content-Type:[application/json; charset=UTF-8]
Date:[Sat, 06 Feb 2021 09:14:05 GMT]
Etag:["MTYxMjU4Njk5MjI3NQ"]
Expires:[Mon, 01 Jan 1990 00:00:00 GMT]
Pragma:[no-cache]
Server:[GSE]
Vary:[Origin X-Origin]
X-Content-Type-Options:[nosniff]
X-Frame-Options:[SAMEORIGIN]
X-Xss-Protection:[1; mode=block]]}
ForceSendFields:[]
NullFields:[]
}

One can easily find all the parameters related to and given element inside a google drive folder by parsing the element id. Feel free to go through the output stated above and use it as you please.

I have also shared the entire code block at the end for just in case :)

Feel free to ask doubts or suggest changes by adding a response

©one_eyed_coder

If you enjoyed this poem help others find it by holding the 👏 button until the heavens drop. You can give up to 50 👏

package mainimport ("encoding/json""fmt""io/ioutil""log""net/http""os""strings""golang.org/x/net/context""golang.org/x/oauth2""golang.org/x/oauth2/google""google.golang.org/api/drive/v2")func main(){
// Initializing google drive API interaction
b, err := ioutil.ReadFile("credentials.json")if err != nil {log.Fatalf("Unable to read client secret file: %v", err)}// If modifying these scopes, delete your previously saved token.json.config, err := google.ConfigFromJSON(b, drive.DriveReadonlyScope)
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
// If modifying these scopes, delete your previously saved token.json.config, err := google.ConfigFromJSON(b, drive.DriveReadonlyScope)if err != nil {log.Fatalf("Unable to parse client secret file to config: %v", err)}srv, err := drive.New(client)if err != nil {log.Fatalf("Unable to retrieve Drive client: %v", err)}elements, err := AllChildren(srv,"1g7ZEi_3l3aTxUTDTjRGSZ3Xl_dPjSIIb")if err != nil {log.Fatalf("Unable to retrieve Zone folders: %v", err)}for _, element:= range elements{elementFeilds, err := fileDetails(srv, element.Id)if err != nil {log.Fatalf("Unable to retrieve Drive folder's name: %v", err)}fmt.Printf("%+v\n", elementFeilds)fmt.Scanln()}}// AllChildren fetches all the children of a given folderfunc AllChildren(d *drive.Service, folderID string) ([]*drive.ChildReference,error) {var cs []*drive.ChildReferencepageToken := ""for {q := d.Children.List(folderID)// If we have a pageToken set, apply it to the queryif pageToken != "" {q = q.PageToken(pageToken)}r, err := q.Do()if err != nil {fmt.Printf("An error occurred: %v\n", err)return cs, err}cs = append(cs, r.Items...)pageToken = r.NextPageTokenif pageToken == "" {break}}return cs, nil}// PrintFile fetches the given filefunc fileDetails(d *drive.Service, fileID string) (*drive.File, error) {f, err := d.Files.Get(fileID).Do()if err != nil {return nil, err}return f, err}

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

One Eyed Astericks
One Eyed Astericks

Published in One Eyed Astericks

Got a piece you wish to share, message us and we will get back to you with in a week.

Calm Ninja
Calm Ninja

Written by Calm Ninja

Trying to find the true meaning of word human.

Responses (1)

Write a response