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
1.2 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:
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/
.
Check Firebase Console:
– Database:
– Storage:
IV. Source Code
Angular5FirebaseStorage-list-files
Last updated on November 29, 2018.
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:
The rules on my database:
Any help is greatly appreciated.
Hi Marty,
Please notice that I use Firebase Realtime Database, not Firestore 🙂
Regards,
JSA.
How to dynamically set private variable base path in upload file service
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’.
Hi, Thanks For the awesome tutorial, it worked for me
Iam getting the following error
Invalid argument in `on` at index 0: Expected one of the event types: [state_changed].”,”serverResponse_”:null,”name_”:”FirebaseError