@angular/fire/storage Angular 11 Upload files to Firebase

Angular 11 upload files to firebase storage

Tutorial: @angular/fire/storage – Angular 11 Upload/Display/Delete files to/from Firebase Storage using @angular/fire

Sourcecode at GitHub: Angular Upload Files to Firebase Storage

In this tutorial, ozenero.com shows you way to upload, get, delete Files to/from Firebase Storage in a simple Angular 11 App using @angular/fire. Files’ info will be stored in Firebase Realtime Database.


Technologies – Angular 11 Upload File to Firebase Storage

– Angular 11
– RxJs 6
– @angular/fire 5.1.3
– firebase 5.11.1

Overview Angular 11 Upload file to Firebase Storage

Angular 11 Upload Files to Firebase Storage Demo
Angular 11 Upload Files to Firebase Storage Demo

We will build an Angular 11 Firebase App that can:
– helps user choose file from local and upload it to Firebase Storage
– show progress with percentage
– save file metadata to Firebase Realtime Database
(Functions above from the posts: Upload File to Storage)
– get list Files and display

How to upload file from Angular to Firebase storage using @angular/fire/storage?

Angular 11 Upload Files to Firebase Storage Overview
Angular 11 Upload Files to Firebase Storage Overview

– Upload file:
+ save file to Firebase Cloud Storage
+ retrieve {name, url} of the file from Firebase Cloud Storage
+ save {name, url} to Firebase Realtime Database

– Get/delete files: use file {name, url} stored in Database as reference to Firebase Cloud Storage.

So, after upload process, the results will be like:

-> Firebase Storage:

Angular 11 Firebase Storage Results
Angular 11 Firebase Storage Results

-> Firebase Realtime Database:

Angular 11 Firebase Storage Database Results
Angular 11 Firebase Storage Database Results

Integrate Firebase into Angular 11 App

Please visit this post to know step by step.

Define Angullar Model Class

We define FileUpload class with fields: key, name, url, file:


export class FileUpload {
  key: string;
  name: string;
  url: string;
  file: File;

  constructor(file: File) {
    this.file = file;
  }
}

@angular/fire/storage – Angular Upload File to Firebase Storage

Uploading a File to Firebase includes 2 action:
– upload file to Firebase Storage.
– save file’s info to Firebase Database.


private basePath = '/uploads';

constructor(private db: AngularFireDatabase, private storage: AngularFireStorage) { }

pushFileToStorage(fileUpload: FileUpload): Observable {
  const filePath = `${this.basePath}/${fileUpload.file.name}`;
  const storageRef = this.storage.ref(filePath);
  const uploadTask = this.storage.upload(filePath, fileUpload.file);

  uploadTask.snapshotChanges().pipe(
    finalize(() => {
      storageRef.getDownloadURL().subscribe(downloadURL => {
        console.log('File available at', downloadURL);
        fileUpload.url = downloadURL;
        fileUpload.name = fileUpload.file.name;
        this.saveFileData(fileUpload);
      });
    })
  ).subscribe();

  return uploadTask.percentageChanges();
}

private saveFileData(fileUpload: FileUpload) {
  this.db.list(this.basePath).push(fileUpload);
}

We use upload() method that returns an AngularFireUploadTask for monitoring.
AngularFireUploadTask has snapshotChanges() method for emitings the raw UploadTaskSnapshot when the file upload progresses.


// get notified when the download URL is available
uploadTask.snapshotChanges().pipe(
    finalize(() => this.downloadURL = fileRef.getDownloadURL() )
 )
.subscribe();

To get the url of the uploaded file, we use the finalize() method from RxJS with getDownloadURL() doesn’t rely on the task.

Finally, we return the upload completion percentage as Observable<number> using AngularFireUploadTask‘s percentageChanges() method.

@angular/fire/storage – Angular retrieve List of Files from Firebase Storage

We get the list from Firebase Storage using files’info (name/url) stored in Firebase Realtime Database:


getFileUploads(numberItems): AngularFireList {
  return this.db.list(this.basePath, ref =>
    ref.limitToLast(numberItems));
}

Display List of Fibase Files in Angular client by using @angular/fire/storage

With getFileUploads() method, we can get list of FileUploads including the key/id in Firebase Realtime Database.

We will use snapshotChanges().pipe(map()) to store the key, it’s so important to use this key for removing individual item (FileUpload):


this.uploadService.getFileUploads(6).snapshotChanges().pipe(
  map(changes =>
    changes.map(c => ({ key: c.payload.key, ...c.payload.val() }))
  )
).subscribe(fileUploads => {
  this.fileUploads = fileUploads;
});

Angular Delete File from Firebase Storage

We delete file with 2 steps:
– delete file’s info from Database
– delete file from Storage


deleteFileUpload(fileUpload: FileUpload) {
  this.deleteFileDatabase(fileUpload.key)
    .then(() => {
      this.deleteFileStorage(fileUpload.name);
    })
    .catch(error => console.log(error));
}

private deleteFileDatabase(key: string) {
  return this.db.list(this.basePath).remove(key);
}

private deleteFileStorage(name: string) {
  const storageRef = this.storage.ref(this.basePath);
  storageRef.child(name).delete();
}

Angular Firebase Storage Project Structure Overview

Angular 11 Upload files to Firebase Storage Turorial Project Structure
Angular 11 Upload files to Firebase Storage Turorial Project Structure

Do Integrate Firebase into Angular 11 App

Please visit this post to know step by step.

*Note: Don’t forget to set RULES for public read/write Database and Storage.

Angular 11 Firebase Storage Rules
Angular 11 Firebase Storage Rules

Add Firebase config to environments variable

Open /src/environments/environment.ts file, add your Firebase configuration:


export const environment = {
  production: false,
  firebase: {
    apiKey: 'xxx',
    authDomain: 'gkz-angular-firebase.firebaseapp.com',
    databaseURL: 'https://gkz-angular-firebase.firebaseio.com',
    projectId: 'gkz-angular-firebase',
    storageBucket: 'gkz-angular-firebase.appspot.com',
    messagingSenderId: '123...'
  }
};

Create Angular Service & Components

Run the commands below:
ng g s upload/UploadFile
ng g c upload/FormUpload
ng g c upload/ListUpload
ng g c upload/DetailsUpload

Setup Angular @NgModule

app.module.ts


import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AngularFireModule } from '@angular/fire';
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { AngularFireStorageModule } from '@angular/fire/storage';
import { environment } from '../environments/environment';

import { AppComponent } from './app.component';
import { FormUploadComponent } from './upload/form-upload/form-upload.component';
import { ListUploadComponent } from './upload/list-upload/list-upload.component';
import { DetailsUploadComponent } from './upload/details-upload/details-upload.component';

@NgModule({
  declarations: [
    AppComponent,
    FormUploadComponent,
    ListUploadComponent,
    DetailsUploadComponent
  ],
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFireDatabaseModule,
    AngularFireStorageModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Create Angular Model Class

fileupload.ts


export class FileUpload {
  key: string;
  name: string;
  url: string;
  file: File;

  constructor(file: File) {
    this.file = file;
  }
}

Implement Angular Upload File Service

upload-file.service.ts


import { Injectable } from '@angular/core';
import { AngularFireDatabase, AngularFireList } from '@angular/fire/database';
import { AngularFireStorage } from '@angular/fire/storage';

import { FileUpload } from './fileupload';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class UploadFileService {

  private basePath = '/uploads';

  constructor(private db: AngularFireDatabase, private storage: AngularFireStorage) { }

  pushFileToStorage(fileUpload: FileUpload): Observable {
    const filePath = `${this.basePath}/${fileUpload.file.name}`;
    const storageRef = this.storage.ref(filePath);
    const uploadTask = this.storage.upload(filePath, fileUpload.file);

    uploadTask.snapshotChanges().pipe(
      finalize(() => {
        storageRef.getDownloadURL().subscribe(downloadURL => {
          console.log('File available at', downloadURL);
          fileUpload.url = downloadURL;
          fileUpload.name = fileUpload.file.name;
          this.saveFileData(fileUpload);
        });
      })
    ).subscribe();

    return uploadTask.percentageChanges();
  }

  private saveFileData(fileUpload: FileUpload) {
    this.db.list(this.basePath).push(fileUpload);
  }

  getFileUploads(numberItems): AngularFireList {
    return this.db.list(this.basePath, ref =>
      ref.limitToLast(numberItems));
  }

  deleteFileUpload(fileUpload: FileUpload) {
    this.deleteFileDatabase(fileUpload.key)
      .then(() => {
        this.deleteFileStorage(fileUpload.name);
      })
      .catch(error => console.log(error));
  }

  private deleteFileDatabase(key: string) {
    return this.db.list(this.basePath).remove(key);
  }

  private deleteFileStorage(name: string) {
    const storageRef = this.storage.ref(this.basePath);
    storageRef.child(name).delete();
  }
}

Create Angular Component with Upload Form

form-upload.component.ts


import { Component, OnInit } from '@angular/core';
import { UploadFileService } from '../upload-file.service';
import { FileUpload } from '../fileupload';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-form-upload',
  templateUrl: './form-upload.component.html',
  styleUrls: ['./form-upload.component.css']
})
export class FormUploadComponent implements OnInit {

  selectedFiles: FileList;
  currentFileUpload: FileUpload;
  percentage: number;

  constructor(private uploadService: UploadFileService) { }

  ngOnInit() {
  }

  selectFile(event) {
    this.selectedFiles = event.target.files;
  }

  upload() {
    const file = this.selectedFiles.item(0);
    this.selectedFiles = undefined;

    this.currentFileUpload = new FileUpload(file);
    this.uploadService.pushFileToStorage(this.currentFileUpload).subscribe(
      percentage => {
        this.percentage = Math.round(percentage);
      },
      error => {
        console.log(error);
      }
    );
  }

}

form-upload.component.html


<div *ngIf="currentFileUpload" class="progress">
  <div class="progress-bar progress-bar-info progress-bar-striped"
    role="progressbar" attr.aria-valuenow="{{percentage}}"
    aria-valuemin="0" aria-valuemax="100"
    [ngStyle]="{width:percentage+'%'}">
    {{percentage}}%</div>
</div>
 
<label class="btn btn-default"> <input type="file"
  (change)="selectFile($event)">
</label>
 
<button class="btn btn-success" [disabled]="!selectedFiles"
  (click)="upload()">Upload</button>

Create Angular Component for Item Details

details-upload.component.ts


import { Component, OnInit, Input } from '@angular/core';
import { FileUpload } from '../fileupload';
import { UploadFileService } from '../upload-file.service';

@Component({
  selector: 'app-details-upload',
  templateUrl: './details-upload.component.html',
  styleUrls: ['./details-upload.component.css']
})
export class DetailsUploadComponent implements OnInit {

  @Input() fileUpload: FileUpload;

  constructor(private uploadService: UploadFileService) { }

  ngOnInit() {
  }

  deleteFileUpload(fileUpload) {
    this.uploadService.deleteFileUpload(fileUpload);
  }

}

details-upload.component.html


<a href="{{fileUpload.url}}">{{fileUpload.name}}</a>
<button (click)='deleteFileUpload(fileUpload)' class="btn btn-danger btn-xs" style="float: right">Delete</button>

Create Angular Component to display List of Uploaded files

list-upload.component.ts


import { Component, OnInit } from '@angular/core';
import { map } from 'rxjs/operators';

import { UploadFileService } from '../upload-file.service';

@Component({
  selector: 'app-list-upload',
  templateUrl: './list-upload.component.html',
  styleUrls: ['./list-upload.component.css']
})
export class ListUploadComponent implements OnInit {

  fileUploads: any[];

  constructor(private uploadService: UploadFileService) { }

  ngOnInit() {
    // Use snapshotChanges().pipe(map()) to store the key
    this.uploadService.getFileUploads(6).snapshotChanges().pipe(
      map(changes =>
        changes.map(c => ({ key: c.payload.key, ...c.payload.val() }))
      )
    ).subscribe(fileUploads => {
      this.fileUploads = fileUploads;
    });
  }

}

list-upload.component.html


<div class="panel panel-primary">
  <div class="panel-heading">List of Files</div>
  <div *ngFor="let file of fileUploads">
    <div class="panel-body">
      <app-details-upload [fileUpload]='file'></app-details-upload>
    </div>
  </div>
</div>

Embed Angular Components in App Component

app.component.ts


import { Component } from '@angular/core';

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

app.component.html


<div class="container" style="width:400px">
  <div style="color: blue; margin-bottom: 20px">
    <h1>{{title}}</h1>
    <h3>{{description}}</h3>
  </div>

  <app-form-upload></app-form-upload>

  <br />
  <br />

  <app-list-upload></app-list-upload>
</div>

Further Reading

Firease Storage Upload Files

Reading More:


Source Code for Angular Upload File to Firebase Storage using @angular/fire/storage

Sourcecode for “Angular 11 upload files to Firebase Storage”:

Angular-11-Upload-File-Firebase-Storage

– Github:

Angular Upload Files to Firebase Storage

Leave a Reply

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