Angular 5 Firebase – Upload/Display/Delete Files from Storage

In the post, we have known how to upload file to Firebase Storage. This tutorial shows you way to upload, get, delete Files in a simple Angular 5 App.

I. Technology

– Angular 5
– AngularFire2 5.0

II. How to do

1. Set up the Firebase Project & Install AngularFire2

Please visit this post to know step by step.

2. Upload File Service

– upload file to Storage and file’s info to Database.
– get List Files from Firebase Storage by files’info (name/url) stored in Database.
– delete file with 2 steps: delete file’s info from Database, then delete file from Storage

export class UploadFileService {

  private basePath = '/uploads';

  constructor(private db: AngularFireDatabase) { }

  pushFileToStorage(fileUpload: FileUpload, progress: { percentage: number }) {
    const storageRef = firebase.storage().ref();
    storageRef.child(`${this.basePath}/${fileUpload.file.name}`).put(fileUpload.file);

    fileUpload.url = downloadURL;
    fileUpload.name = fileName;
    this.saveFileData(fileUpload);
  }

  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 = firebase.storage().ref();
    storageRef.child(`${this.basePath}/${name}`).delete();
  }
}

To know how to save file info to Firebase Realtime Database, please visit previous post:
Angular 4 Firebase – Upload File to Storage

3. Get and display List of Files
import { FileUpload } from '../fileupload';
import { UploadFileService } from '../upload-file.service';

export class ListUploadComponent implements OnInit {

  fileUploads: any[];

  constructor(private uploadService: UploadFileService) { }

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

}

HTML template:

{{file.name}}

III. Practice

1. Project Overview
1.1 Goal

We will build an Angular 5 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: Angular 4 Firebase – Upload File to Storage)
– get list Files and display

angular-5-firebase-upload-get-files-storage-demo

1.2 Structure

angular-5-firebase-upload-get-images-storage-structure

2. Step by step
2.1 Set up the Firebase Project & Install AngularFire2

Please visit this post to know step by step.

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

2.2 Add Firebase config to environments variable

Open /src/environments/environment.ts, add your Firebase configuration that we have saved when Popup window was shown:

export const environment = {
  production: false,
  firebase: {
    apiKey: 'xxx',
    authDomain: 'jsa-angular5.firebaseapp.com',
    databaseURL: 'https://jsa-angular5.firebaseio.com',
    projectId: 'jsa-angular5',
    storageBucket: 'xxxbucket.appspot.com',
    messagingSenderId: 'xxx'
  }
};
2.3 Setup @NgModule
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AngularFireDatabaseModule } from 'angularfire2/database';
import { AngularFireModule } from 'angularfire2';
import { environment } from '../environments/environment';

import { AppComponent } from './app.component';

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

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
  ],
  providers: [UploadFileService],
  bootstrap: [AppComponent]
})
export class AppModule { }
2.4 Model Class
export class FileUpload {

  key: string;
  name: string;
  url: string;
  file: File;

  constructor(file: File) {
    this.file = file;
  }
}
2.5 Service
import { Injectable } from '@angular/core';
import { AngularFireDatabase, AngularFireList } from 'angularfire2/database';
import * as firebase from 'firebase';

import { FileUpload } from './fileupload';

@Injectable()
export class UploadFileService {

  private basePath = '/uploads';

  constructor(private db: AngularFireDatabase) { }

  pushFileToStorage(fileUpload: FileUpload, progress: { percentage: number }) {
    const storageRef = firebase.storage().ref();
    const uploadTask = storageRef.child(`${this.basePath}/${fileUpload.file.name}`).put(fileUpload.file);

    uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED,
      (snapshot) => {
        // in progress
        const snap = snapshot as firebase.storage.UploadTaskSnapshot;
        progress.percentage = Math.round((snap.bytesTransferred / snap.totalBytes) * 100);
      },
      (error) => {
        // fail
        console.log(error);
      },
      () => {
        // success
        fileUpload.url = uploadTask.snapshot.downloadURL;
        fileUpload.name = fileUpload.file.name;
        this.saveFileData(fileUpload);
      }
    );
  }

  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 = firebase.storage().ref();
    storageRef.child(`${this.basePath}/${name}`).delete();
  }
}
2.6 Form Upload Compoment
import { Component, OnInit } from '@angular/core';

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

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

  selectedFiles: FileList;
  currentFileUpload: FileUpload;
  progress: { percentage: number } = { percentage: 0 };

  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, this.progress);
  }
}

form-upload.component.html:

{{progress.percentage}}%
2.7 DetailsUpload Component

details-upload.component.ts

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

@Component({
  selector: '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

{{fileUpload.name}}

2.8 ListUpload Component

list-upload.component.ts

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

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

@Component({
  selector: '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().map() to store the key
    this.uploadService.getFileUploads(6).snapshotChanges().map(changes => {
      return changes.map(c => ({ key: c.payload.key, ...c.payload.val() }));
    }).subscribe(fileUploads => {
      this.fileUploads = fileUploads;
    });
  }

}

list-upload.component.html

List of Files
2.9 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 = 'JavaSampleApproach';
  description = 'Angular5-Firebase Demo';
}

app.component.html

{{title}}

{{description}}



2.10 Check Result

Run the App, go to http://localhost:4200/.

angular-5-firebase-upload-get-files-storage-demo

Check Firebase Console:
– Database:

angular-5-firebase-upload-get-images-storage-result-database

– Storage:

angular-5-firebase-upload-get-images-storage-result-storage

IV. Source Code

Angular5FirebaseStorage-list-files

7 thoughts on “Angular 5 Firebase – Upload/Display/Delete Files from Storage”

  1. I am able to upload files, but get the following error when the list component tries to display files:

    ERROR Error: Uncaught (in promise): Error: PERMISSION_DENIED: Permission denied
    Error: PERMISSION_DENIED: Permission denied

    The rules on my storage:

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

    The rules on my database:

    service cloud.firestore {
      match /databases/{database}/documents {
        match /{document=**} {
          allow read, write;
        }
      }
    }
    

    Any help is greatly appreciated.

  2. ERROR in src/app/upload/list-upload/list-upload.component.ts(20,48): error TS2339: Property ‘key’ does not exist on type ‘DatabaseSnapshot’.
    Property ‘key’ does not exist on type ‘DatabaseSnapshotExists’.

  3. Iam getting the following error
    Invalid argument in `on` at index 0: Expected one of the event types: [state_changed].”,”serverResponse_”:null,”name_”:”FirebaseError

  4. Hey I know this is off topic but I was wondering if you knew of any
    widgets I could add to my blog that automatically tweet my newest twitter updates.
    I’ve been looking for a plug-in like this for quite some time and was hoping maybe you would have some experience with something like this.

    Please let me know if you run into anything. I truly enjoy
    reading your blog and I look forward to your new updates.

Leave a Reply

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