Angular-TypescriptNo Comments

Uploading file on Google Drive with progress bar using Google APIs

Hey Learners! In this article we will discuss how to upload file on Google Drive using Google APIs along with progress bar feedback. The prerequisite for making any Google API call is to have an “access_token”, if you want to know how to retrieve Access Token using Google OAuth 2.0, follow this post.


Demo:-


Code Structure:-

Now lets get out hands dirty and invoke the Google Drive Api for file upload. File upload can be done in 3 ways:-

1. Simple upload:- Small file upload without metadata.
2. Multipart upload:- Small file upload with metadata(with file name etc).
3. Resumable upload:- Use this method to upload files greater than 5 MB. Allows to send metadata also.


In this post we are going to cover Multipart & Resumable upload as with Simple upload, we cannot name our files in Google Drive.


Lets see Multipart upload first.


Multipart Upload


For multipart upload, we create FormData, and send the ‘file’ and the ‘metadata’ inside it.

  /**
   * Performs multipart Google Drive file upload with metadata.
   * Can upload files upto 5 MB or less, as per drive API.
   *
   * @param file
   */
  uploadFileWithMetadata(file: File): Observable<HttpEvent<DriveUploadResponse>> {
    let headers = new HttpHeaders({
      'Authorization': `Bearer ${this.getAccessToken()}`
    });
    const metadata = {
      name: file.name,
    };
    const formData: FormData = new FormData();
    formData.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'}));
    formData.append('file', file);

    return this.http.post<DriveUploadResponse>(DRIVE_FILE_UPLOAD_MULTIPART_ENDPOINT, formData, {
      headers: headers,
      reportProgress: true,
      observe: 'events'
    });
  }


DRIVE_FILE_UPLOAD_MULTIPART_ENDPOINT is “https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart”. Note that we are observing events with reportProgress as true, so that we can inform the user of the feedback of file upload progress.

We receive DriveUploadResponse type as response from the above POST call. We use the id field from this, to construct the Google Drive URL which can be used for download.

export interface DriveUploadResponse {
  kind: string,
  id: string,
  name: string,
  mimeType: string,
}


Lets see Resumbale upload now.


Resumable Upload


Resumable Upload is a two step process:-

1. In first step we send the metadata and retrieve the location URI where the file has to be uploaded. Note: The location is received in the response headers.
2. We use the location from the first response, to send the file for upload.

  /**
   * Retrieves the location in which the file has to be uploaded.
   *
   * @param file
   */
  getLocationForLargeFileUpload(file: File): Observable<HttpResponse<any>> {
    let headers = new HttpHeaders({
      'Authorization': `Bearer ${this.getAccessToken()}`
    });
    const metadata = {
      name: file.name,
    };
    const formData: FormData = new FormData();
    formData.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'}));

    return this.http
      .post <HttpResponse<any>>(DRIVE_FILE_UPLOAD_RESUMABLE_ENDPOINT, JSON.stringify(metadata), {
        headers: headers,
        observe: 'response'
      });
  }


From the above HttpResponse, we extract the headers and get location from it, then we make a call to uploadLargeFile() as below.

  /**
   * Uploads files larger than 5 MB to Google Drive.
   *
   * @param location
   * @param file
   */
  uploadLargeFile(location: string, file: File): Observable<HttpEvent<DriveUploadResponse>> {
    return this.http.put<DriveUploadResponse>(location, file, {
      reportProgress: true,
      observe: 'events'
    });
  }


We use concatMap() to invoke uploadLargeFile() after receiving the response from getLocationForLargeFileUpload().

  uploadLargeFile(file: File, fileDetails: FileDetails) {
    this.driveService.getLocationForLargeFileUpload(file)
      .pipe(
        concatMap(response => this.driveService.uploadLargeFile(response.headers.get('location') || '', file))
      )
      .pipe(tap(event => {
        if (event.type === HttpEventType.UploadProgress) {
          // @ts-ignore
          fileDetails.progress = Math.round(100 * event.loaded / event.total);
        }
      }))
      .subscribe((response: HttpEvent<DriveUploadResponse>) => {
        if (response instanceof HttpResponse) {
          const driveResponse: DriveUploadResponse | null = response.body;
          if (driveResponse) {
            fileDetails.driveUrl = DRIVE_URL_OF_FILE + driveResponse.id;
          }
        }
      }, e => {
        this.snackBar.open(e.error.error.message, 'Ok');
      });
  }


Get the entire codebase from GitHub.

git Download the code from GitHub

Keep Learning! Stay Sharp!