Angular

Firebase Cloud Storage With Angular


Subscribe On YouTube

DemoCode

Firebase Cloud Storage is a cloud service that let’s you store and retrieve user generated content (like images, audio, or video files). The service is highly scaleable and part of the Firebase platform, so that you can make use of the Firebase Cloud Service on various platforms.


If you like CodingTheSmartWay, then consider supporting us via Patreon. With your help we’re able to release developer tutorial more often. Thanks a lot!


If you want to use Firebase service in your Angular application the AngularFire2 library is your choice, as this is the official library. In the most recent version of AngularFire2 the support of Firebase Cloud Services have been added.

Now, managing files in your Angular application by using Firebase Cloud Service got a lot easier. Let’s try out a practical example.

Initiating A New Angular Project And Adding Firebase And AngularFire2

In the first step we’re going to initiate a new Angular 5 project by using Angular CLI in the following way:

$ ng new ngcloudstorage

Change into the newly created project folder next:

$ cd ngcloudstorage

Check if everything is working by starting up the development web server:

$ ng serve

And finally try to access the default Angular application in the browser.

Adding Firebase / AngularFire2 to your project is easy. Install both packages by using NPM:

$ npm i firebase angularfire2

Now the project setup is ready and in the next section we’re able to start with the implementation.

Add AngularFireModule and AngularFireStorageModule

To make use of Firebase and AngularFire2 within the Angular project we need to make AngularFireModule and AngularFireStorageModule available. To do so, let’s add the following import statements in file app.module.ts first:

import { AngularFireModule } from 'angularfire2';
import { AngularFireStorageModule } from 'angularfire2/storage';

Next you need to add both modules to the imports array as well:

  imports: [
    BrowserModule,
    AngularFireModule.initializeApp({
      apiKey: "<your-api-key>",
      authDomain: "<your-auth-domain>",
      storageBucket: "<project-id>.appspot.com",
      projectId: "<your-project-id>",
    }),
    AngularFireStorageModule
  ],

The AngularFireModule is added by calling the factory method initializeApp. Calling this method requires us to pass over the Firebase configuration as an object. The object contains the following properties: apiKey, authDomain, storageBucket and projectId. The values of these configuration properties needs to set according to your Firebase account. To look it up login to the Firebase backend (Firebase console), create a new Firebase project and then click on the link Add Firebase to your web app.


A popup window opens in which you can see the relevant key-value pairs.

Inject AngularFireStorage Service Into Component AppComponent

Now that the modules are made available in our Angular application we’re ready to inject AngularFireStorage into component AppComponent in file app.component.ts:

import { Component } from '@angular/core';
import { AngularFireStorage } from 'angularfire2/storage';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';

  constructor(private afStorage: AngularFireStorage) { }
}

AppComponent Template

As we’re going to use Bootstrap 4 CCS classes in the template implementation we need to make sure to add Bootstrap to our project. This can be done in two steps. First of all install Bootstrap via NPM:

$ npm i bootstrap

Next, include the Bootstrap CSS file in styles.css by adding the following line of code:

@import '~bootstrap/dist/css/bootstrap.min.css';

Now the Bootstrap CSS file is included and we can make use of Bootstrap’s CSS classes in the template implementation.

Insert the following template code in file app.component.html:

<div class="container">
  <div class="card">
    <div class="card-header">
      Firebase Cloud Storage & Angular 5
    </div>
    <div class="card-body">
      <h5 class="card-title">Select a file for upload:</h5>
      <input type="file" (change)="upload($event)" accept=".png,.jpg" />
    </div>
  </div>
</div>

If you now access the application in the browser you should be able to see the following output:


The user can use the button to the select a file from the computer for download. As we’ve connected the upload($event) handler method to the change event of the file input control we need to implement this method next to handle the upload process and store the file in Firebase Cloud Storage.

Handling the Upload

Now we need to implement method upload(event) in class AppComponent.

import { Component } from '@angular/core';
import { AngularFireStorage, AngularFireStorageReference, AngularFireUploadTask } from 'angularfire2/storage';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  ref: AngularFireStorageReference;
  task: AngularFireUploadTask;

  constructor(private afStorage: AngularFireStorage) { }

  upload(event) {
    const id = Math.random().toString(36).substring(2);
    this.ref = this.afStorage.ref(id);
    this.task = this.ref.put(event.target.files[0]);
  }
}

On class level two members are created, one of type AngularFireStorageReference and one of type AngularFireUploadTask. Inside the upload method we’re first creating a unique identifier which is then used to create a new Firebase storage reference by calling the ref method and passing in the id.

With that storage reference available we’re then able to upload the file which has been selected by the user by using the following line of code:

this.task = this.ref.put(event.target.files[0]);

Firebase Storage Security Rules

Now we’re nearly ready to try it out, there is just one step left to complete. Therefore open the Firebase console for your project once again and select from the left-side menu the link Storage. In the Storage view select tab Rules:


Change the default rules to the following to enable upload files without needing to authenticate first:

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write;
    }
  }
}

Now we’re ready to upload the first file. The result should then be visible in the Files tab of the Firebase Storage site:

Tracking The Upload Progress

Now that the uploading of documents to Firebase Cloud Storage is working, we’re ready to further extend the application. The next feature which should be added is a progress bar showing the progress of the upload process. Again we’re using some of Bootstrap’s CSS classes to include a progress bar in the template code of AppComponent:

<div class="progress">
    <div class="progress-bar progress-bar-striped bg-success" role="progressbar" [style.width]="(uploadProgress | async) + '%'" [attr.aria-valuenow]="(uploadProgress | async)" aria-valuemin="0" aria-valuemax="100"></div>
</div>

The progress is set by binding style.width to the value of uploadProgress. This observable has not been defined yet, so we need to further extend the implementation in app.component.ts. First let’s import Observable from the RxJS library by adding the following line of code:

import { Observable } from 'rxjs/Observable';

Add the following class member to class AppComponent:

uploadProgress: Observable<number>;

The variable uploadProgress is declared as an Observable of type number. Now we can use this variable within method upload in the following way:

  upload(event) {
    const id = Math.random().toString(36).substring(2);
    this.ref = this.afStorage.ref(id);
    this.task = this.ref.put(event.target.files[0]);
    this.uploadProgress = this.task.percentageChanges();
  }

The call of this.task.percentageChanges() is returning an Observable of type number which gives us access to the current progress, so that the progress bar is filling up as the upload process runs.

Showing The Upload Result

To finalize the sample application let’s extend the output (in file app.component.html) to also display the link to the file in Firebase Cloud Storage once the upload has been finished successfully. Add the following code to the template:

<div *ngIf="downloadURL | async; let downloadSrc" class="alert alert-info" role="alert">
    File uploaded: <a [href]="downloadSrc">{{downloadSrc}}</a>
</div>

In app.component.ts we now need to declare the downloadURL class member with type Observable<string>.

downloadURL: Observable<string>;

Finally the implementation of the upload method has to be extended to set downloadURL to the Observable which is returned from calling method this.task.downloadURL:

  upload(event) {
    const id = Math.random().toString(36).substring(2);
    this.ref = this.afStorage.ref(id);
    this.task = this.ref.put(event.target.files[0]);
    this.uploadProgress = this.task.percentageChanges();
    this.downloadURL = this.task.downloadURL();
  }

Controlling The Upload Process

The running upload process can be further controlled. If you want to give the user full control of the process (pause, cancel and resume) you first need to include the following set of buttons in app.component.html:

<div class="btn-group" role="group" *ngIf="uploadState | async; let state">
    <button type="button" class="btn btn-primary" (click)="task.pause()" [disabled]="state === 'paused'">Pause</button>
    <button type="button" class="btn btn-primary" (click)="task.cancel()" [disabled]="!(state === 'paused' || state === 'running')">Cancel</button>
    <button type="button" class="btn btn-primary" (click)="task.resume()" [disabled]="state === 'running'">Resume</button>
</div>

The click events of those buttons are bound to the respective methods of the AngularFireUploadTask object:

  • task.pause(): Pausing the current upload
  • task.cancel(): Cancel the current upload
  • task.cancel(): Resume the current upload if it has been paused before

Controlling the state of the buttons (enable / disable) helps to let the user know which action is available. In the template code from above the disabled property of the button has been bound to the following expressions:

  • Button Pause: state === 'paused'
  • Button Cancel: !(state === 'paused' || state === 'running')
  • Button Resume: state === 'running'

These checks require access to a variable with name uploadState. Let’s declare this variable in class AppComponent:

uploadState: Observable<string>;

And make sure that this variable is initialized in the upload method by calling the snapshotChanges method of the AngularFireUploadTask object:

  upload(event) {
    const id = Math.random().toString(36).substring(2);
    this.ref = this.afStorage.ref(id);
    this.task = this.ref.put(event.target.files[0]);
    this.uploadState = this.task.snapshotChanges().pipe(map(s => s.state));
    this.uploadProgress = this.task.percentageChanges();
    this.downloadURL = this.task.downloadURL();
  }

Here we’re making use of the map operator to extract the state information from the task snapshot. This operator is part of the RxJS library. So you need to make sure that the following import statement is added:

import { map } from 'rxjs/operators/map';

Now the final result should look like the following:


Just try out the upload different sizes of PNG / JPEG files and control the upload process by using the buttons Pause, Cancel and Resume.

Complete Code

The summarize, let’s take a look at the complete code of our sample application.

app.component.ts:

import { Component } from '@angular/core';
import { AngularFireStorage, AngularFireStorageReference, AngularFireUploadTask } from 'angularfire2/storage';
import { Observable } from 'rxjs/Observable';
import { map } from 'rxjs/operators/map';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  ref: AngularFireStorageReference;
  task: AngularFireUploadTask;
  uploadState: Observable<string>;
  uploadProgress: Observable<number>;
  downloadURL: Observable<string>;

  constructor(private afStorage: AngularFireStorage) { }

  upload(event) {
    const id = Math.random().toString(36).substring(2);
    this.ref = this.afStorage.ref(id);
    this.task = this.ref.put(event.target.files[0]);
    this.uploadState = this.task.snapshotChanges().pipe(map(s => s.state));
    this.uploadProgress = this.task.percentageChanges();
    this.downloadURL = this.task.downloadURL();
  }
}

app.component.html:

<div class="container">
  <div class="card">
    <div class="card-header">
      Firebase Cloud Storage & Angular 5
    </div>
    <div class="card-body">
      <h5 class="card-title">Select a file for upload:</h5>
      <input type="file" (change)="upload($event)" accept=".png,.jpg" />
      <br><br>
      <div class="progress">
        <div class="progress-bar progress-bar-striped bg-success" role="progressbar" [style.width]="(uploadProgress | async) + '%'" [attr.aria-valuenow]="(uploadProgress | async)" aria-valuemin="0" aria-valuemax="100"></div>
      </div>
      <br>
      <div *ngIf="downloadURL | async; let downloadSrc" class="alert alert-info" role="alert">
          File uploaded: <a [href]="downloadSrc">{{downloadSrc}}</a>
      </div>
      <br>
      <div class="btn-group" role="group" *ngIf="uploadState | async; let state">
          <button type="button" class="btn btn-primary" (click)="task.pause()" [disabled]="state === 'paused'">Pause</button>
          <button type="button" class="btn btn-primary" (click)="task.cancel()" [disabled]="!(state === 'paused' || state === 'running')">Cancel</button>
          <button type="button" class="btn btn-primary" (click)="task.resume()" [disabled]="state === 'running'">Resume</button>
      </div>
    </div>
  </div>
</div>

ONLINE COURSE: Angular - The Complete Guide

Check out the great Angular – The Complete Guide with thousands of students already enrolled:

Angular – The Complete Guide

  • This course covers Angular 5
  • Develop modern, complex, responsive and scalable web applications with Angular
  • Use their gained, deep understanding of the Angular  fundamentals to quickly establish themselves as frontend developers
  • Fully understand the architecture behind an Angular application and how to use it
  • Create single-page applications with on of the most modern JavaScript frameworks out there

Go To Course

ONLINE COURSE: Angular and Firebase

Check out the great course Angular and Firebase – Build a Web App with Typescript with thousands of students already enrolled:

Angular and Firebase – Build a Web App with Typescript

  • Learn how to build a Web Application using Angular, Firebase and Typescript
  • Confidently build a full stack application using Angular as the frontend and Firebase as the backend
  • Know the basics about Firebase, and more
  • Know how model the data of a Firebase application
  • Understand how to use Angular Fire to build a service layer

Go To Course

 


Using and writing about best practices and latest technologies in web design & development is my passion.