Angular 6 – Google Drive Api v3 example

I am building a website for hosting and streaming some audio books which I found on the Internet. I decide to use Google Drive for storing my audio files because Google allows me to stream directly from his server. The boring steps what I have to do are downloading audio files, uploading them to Google Drive, publish them and get their links and add the links to the audio player in my website. All by hand! So I start to write a small tool for saving my time with all these steps. First I try to upload the files by myself to Google Drive without using his website. In this post, I will write down the steps I’ve done to integrate Google Drive API into an Angular application. My demo application has a pretty simple UI.

1. Credentials

First, we need the credentials for Googe Drive API client library.
– Go to https://console.developers.google.com. Sign in. Create a project for your application. The available projects can be found on the top left corner

– Be sure that Google Drive API has been enabled for your project.

– Go to Credentials section, on the tab OAuth consent screen, enter the required information.

– In Credentials section, on the tab Credentials, create 2 credentials: API key and OAuth client ID

– We’ll need these two credentials for out client library later. Click on the pencil icon of OAuth client ID to edit it

– Add the domains which you allow to access your Google Drive OAuth client ID

2. Install Google Drive API client

The current version of Google Drive API is v3. We can find all available clients at following link https://developers.google.com/drive/api/v3/downloads with their examples. Back to our Angular project, let’s create an Angular project with angular-cli and install following packages for types

npm install --save @types/gapi
npm install --save @types/gapi.auth2
npm install --save @types/gapi.client.drive

These packages provide us the types definitions for Google API and Google Drive API. Then open the index.html and add the Google API script to our Angular application.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>HD Google Drive</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
  <scr1pt src="https://apis.google.com/js/api.js"></script>
</head>
<body class="docs-app-background">
  <app-root></app-root>
</body>
</html>

Now the Google Drive API client is ready to use in your Angular project. I do intentionally a grammar error for the scr1pt opening tags so that the script won’t be rendering inside my post. You need to fix it in your project one.

3. Init and authenticate

After installing Google Drive API, we can initialize and start to authenticate the user with Google Drive API service. I use APP_INITIALIZER to force Google Drive Client to initialize with the application when it starts. In app.module.ts, add following code to initialize the client

export function initGapi(gapiSession: GapiSession) {
  return () => gapiSession.initClient();
}

 providers: [
    { provide: APP_INITIALIZER, useFactory: initGapi, deps: [GapiSession], multi: true },
]

The initClient must be a promise so that Angular knows that he has to wait until client finishes before he continues his app initilization

initClient() {
	return new Promise((resolve,reject)=>{
		gapi.load('client:auth2', () => {
			return gapi.client.init({
				apiKey: API_KEY,
				clientId: CLIENT_ID,
				discoveryDocs: DISCOVERY_DOCS,
				scope: SCOPES,
			}).then(() => {                   
				this.googleAuth = gapi.auth2.getAuthInstance();
				resolve();
			});
		});
	});
	
}

The API_KEY and CLIENT_ID are the credentials which we created before in Google Console Developers. After the client gets initialized, we can now authenticate the user

signIn() {
	return this.googleAuth.signIn({
		prompt: 'consent'
	}).then((googleUser: gapi.auth2.GoogleUser) => {
		this.appRepository.User.add(googleUser.getBasicProfile());
	});
}

4. List and create folders

After the user signed in, we can now list all files of the user on Google Drive.

getFiles(folderId: string) {
	return gapi.client.drive.files.list({
		pageSize: 100,
		fields: "nextPageToken, files(id, name, mimeType, modifiedTime, size)",
		q: `'${folderId}' in parents and trashed = false`
	}).then((res) => {
		let files: FileInfo[] = [];
		res.result.files.forEach((file) => files.push(FileInfo.fromGoogleFile(file)));
		return files;
	});
}

The first level folder “My Drive” has the folder id of “root”. The following code listing just list all files which you can see in “My Drive” folder.

ngOnInit(): void {
	this.appContext.Session.BreadCrumb.init();
	this.breadCrumbItems = this.appContext.Session.BreadCrumb.items;
	this.refresh("root");
}

refresh(fileId: string) {
	this.appContext.Repository.File.getFiles(fileId)
		.then((res) => {
			this.zone.run(() => {
				this.files = res;
				this.dataSource.data = this.files;
			});
		});
}

To create a new folder, just call create function with data structure of v3.

create(parentId: string, folderName: string) {
	var folder = {
		name: folderName,
		mimeType: MIME_TYPE_FOLDER,
		parents: [parentId]
	};
	return gapi.client.drive.files.create({
		resource: folder,
		fields: "id, name, mimeType, modifiedTime, size"
	}).then((res) => {
		return FileInfo.fromGoogleFile(res.result);
	});
}

IMPORTANT: The type definitions of gapi.client.drive has a bug. He doesn’t support the resource parameter. So you have to extend your local type definitions for supporting this parameter. Just add resource parameter to index.d.ts file of @types/gapi.client.drive.

5. Upload files

File uploading to Google Drive is a litte complex. If we want to create a multipart-upload/resumable upload which supports progress notification, we do need a complex uploader. The code of this uploader, you can find in the assets folder of the code repository. Using the this uploader we can upload a file to Google Drive as following

importFile(parentId: string, file: FileInfo, onError: any, onComplete: any, onProgress: any) {
	var contentType = file.Blob.type || 'application/octet-stream';
	var metadata = {
		name: file.Blob.name,
		mimeType: contentType,
		parents: [parentId]
	};

	var uploader = new UploaderForGoogleDrive({
		file: file.Blob,
		token: gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse().access_token,
		metadata: metadata,
		onError: onError,
		onComplete: onComplete,
		onProgress: onProgress,
		params: {
			convert: false,
			ocr: false
		}

	});

	uploader.upload();
}

And we call the upload function with progress notification

this.appContext.Repository.File.importFile(
            this.appContext.Session.BreadCrumb.currentItem.Id,
            this.currentFile,
            (res) => this.onImportError(res),
            (res) => this.onImportComplete(res),
            (res) => this.onImportProgress(res)
        )

6. Source code

Source code: https://bitbucket.org/hintdesk/angular-google-drive-api-example/
Demo: http://googledrive.hintdesk.com

2 thoughts on “Angular 6 – Google Drive Api v3 example”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.