In this tutorial, I will show you how to make File Upload/Display/Delete with Firebase Storage and Angular 10 with the help of @angular/fire
. Files’ info will be stored in Firebase Realtime Database.
Related Posts:
– Angular 10 Firebase Realtime DataBase CRUD example
– Angular 10 Firestore CRUD example
– Angular 10 CRUD Application example with Web API
– Angular 10 Form Validation example (Reactive Forms)
Newer versions:
– Angular 11 File Upload with Firebase Storage
– Angular 12 File Upload with Firebase Storage
– Angular 13 File Upload with Firebase Storage
– Angular 14 File Upload with Firebase Storage
– Angular 15 File Upload with Firebase Storage
– Angular 16 File Upload with Firebase Storage
Contents
Firebase Storage + Angular 10 example Overview
We’re gonna build an Angular 10 App that helps us:
- upload file to Firebase Storage
- see the progress bar with percentage
- save file metadata (name, url) to Firebase Realtime Database
- display list of files
- delete any file in the list
The result in Firebase Cloud Storage:
And Realtime Database:
Data Flow
Following image shows the data flow with Angular Client, Firebase Cloud Storage & Firebase Realtime Database:
– File upload:
- store a file to Firebase Cloud Storage
- retrieve metadata (name, url) of the file from Firebase Cloud Storage
- save metadata (name, url) to Firebase Realtime Database
– Display/get/delete files: use the file metadata (name, url) which is stored in Realtime Database as reference to interact with Firebase Storage.
Define File Upload Class
The FileUpload
class has four fields: key
, name
, url
, file
:
export class FileUpload {
key: string;
name: string;
url: string;
file: File;
constructor(file: File) {
this.file = file;
}
}
File Upload with Firebase Storage
We need to do 2 actions:
- upload file to Firebase Storage.
- save file’s info (name, url) to Firebase Database.
private basePath = '/uploads';
constructor(private db: AngularFireDatabase, private storage: AngularFireStorage) { }
pushFileToStorage(fileUpload: FileUpload): Observable<number> {
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 => {
fileUpload.url = downloadURL;
fileUpload.name = fileUpload.file.name;
this.saveFileData(fileUpload);
});
})
).subscribe();
return uploadTask.percentageChanges();
}
We use upload()
method that returns an AngularFireUploadTask
which provides methods for controlling and monitoring the file upload.
AngularFireUploadTask
has snapshotChanges()
method for emitings the raw UploadTaskSnapshot
when the file upload progresses.
To get the url of the uploaded file, we use the RxJS finalize()
method with getDownloadURL()
which doesn’t rely on the task. So we can get notified when the download URL is available.
uploadTask.snapshotChanges().pipe(
finalize(() => this.downloadURL = fileRef.getDownloadURL() )
)
.subscribe();
Finally, we return the upload completion percentage as Observable<number>
using AngularFireUploadTask
‘s percentageChanges()
method.
return uploadTask.percentageChanges();
Retrieve & Display List of Files from Firebase Storage
We get the list of files’info (name
/url
) from Firebase Realtime Database:
private basePath = '/uploads';
constructor(private db: AngularFireDatabase, ...) { }
getFiles(numberItems): AngularFireList<FileUpload> {
return this.db.list(this.basePath, ref =>
ref.limitToLast(numberItems));
}
With getFiles()
method, we can get list of FileUpload
s including the key
/id
.
Now we’re gonna 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;
});
Delete File from Firebase Storage
There are 2 steps:
- delete file’s info from Database.
- delete file from Storage.
deleteFile(fileUpload: FileUpload): void {
this.deleteFileDatabase(fileUpload.key)
.then(() => {
this.deleteFileStorage(fileUpload.name);
})
.catch(error => console.log(error));
}
private deleteFileDatabase(key: string): void {
return this.db.list(this.basePath).remove(key);
}
private deleteFileStorage(name: string): void {
const storageRef = this.storage.ref(this.basePath);
storageRef.child(name).delete();
}
Implementation
Technology
- Angular 10
- firebase 8
- @angular/fire 6
- rxjs 6
Setup the Firebase Project
Go to Firebase Console, login with your Google Account, then click on Add Project.
You will see the window like this:
Enter Project name, set Project Id and click on Continue.
Turn off Enable Google Analytics for this project, then click Create Project.
Now, browser turns into following view:
If you don’t see it, just choose Project Overview.
Click on Web App, a window will be shown:
Set the nickname and choose Register App for next step.
Copy the script for later use.
Choose Storage on the left (list of Firebase features):
Open Tab Rules, then change allow read, write
value to true
.
Setup Angular 10 Project
Let’s open cmd and use Angular CLI to create a new Angular Project as following command:
ng new Angular10FirebaseStorage
? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? CSS
We also need to generate some Components and Services:
ng g s services/file-upload
ng g c components/upload-form
ng g c components/upload-list
ng g c components/upload-details
ng g class models/file-upload --type=model
Now you can see that our project directory structure looks like this.
Project Structure
Let me explain it briefly.
– environment.ts
configures information to connect with Firebase Project.
– models/file-upload.model.ts
defines the model class.
– services/file-upload.service.ts
exports FileUploadService
that uses @angular/fire
‘s AngularFireStorage
& AngularFireDatabase
to interact with Firebase.
– There are 3 components that uses FileUploadService
:
upload-form
for uploading new fileupload-list
displays list of files, parent ofupload-details
upload-details
shows file details
– app.component
contains the components above.
– app.module.ts
declares Angular components and imports necessary environment & modules.
Integrate Firebase into Angular 10 App
First run the command: npm install firebase @angular/fire
.
Open src/environments/environment.ts, add Firebase configuration that we have saved when Popup window was shown:
export const environment = {
production: false,
firebase: {
apiKey: 'xxx',
authDomain: 'bezkoder-firebase.firebaseapp.com',
databaseURL: 'https://bezkoder-firebase.firebaseio.com',
projectId: 'bezkoder-firebase',
storageBucket: 'bezkoder-firebase.appspot.com',
messagingSenderId: 'xxx',
appId: 'xxx'
}
};
Open app.module.ts, import AngularFireModule
, AngularFireStorageModule
, AngularFireDatabaseModule
and environment
:
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 { UploadFormComponent } from './components/upload-form/upload-form.component';
import { UploadListComponent } from './components/upload-list/upload-list.component';
import { UploadDetailsComponent } from './components/upload-details/upload-details.component';
@NgModule({
declarations: [
AppComponent,
UploadFormComponent,
UploadListComponent,
UploadDetailsComponent
],
imports: [
BrowserModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFireDatabaseModule,
AngularFireStorageModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Create File Upload Class
Let’s create FileUpload
class with 4 fields: key
, name
, url
, file
.
models/file-upload.ts
export class FileUpload {
key: string;
name: string;
url: string;
file: File;
constructor(file: File) {
this.file = file;
}
}
Create File Upload Service
This service will use AngularFireStorage
and AngularFireDatabase
to interact with Firebase Storage and Realtime Database. It contains necessary functions for upload/get/delete file operations.
services/file-upload.service.ts
import { Injectable } from '@angular/core';
import { AngularFireDatabase, AngularFireList } from '@angular/fire/database';
import { AngularFireStorage } from '@angular/fire/storage';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { FileUpload } from '../models/file-upload.model';
@Injectable({
providedIn: 'root'
})
export class FileUploadService {
private basePath = '/uploads';
constructor(private db: AngularFireDatabase, private storage: AngularFireStorage) { }
pushFileToStorage(fileUpload: FileUpload): Observable<number> {
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 => {
fileUpload.url = downloadURL;
fileUpload.name = fileUpload.file.name;
this.saveFileData(fileUpload);
});
})
).subscribe();
return uploadTask.percentageChanges();
}
private saveFileData(fileUpload: FileUpload): void {
this.db.list(this.basePath).push(fileUpload);
}
getFiles(numberItems): AngularFireList<FileUpload> {
return this.db.list(this.basePath, ref =>
ref.limitToLast(numberItems));
}
deleteFile(fileUpload: FileUpload): void {
this.deleteFileDatabase(fileUpload.key)
.then(() => {
this.deleteFileStorage(fileUpload.name);
})
.catch(error => console.log(error));
}
private deleteFileDatabase(key: string): Promise<void> {
return this.db.list(this.basePath).remove(key);
}
private deleteFileStorage(name: string): void {
const storageRef = this.storage.ref(this.basePath);
storageRef.child(name).delete();
}
}
Component for Upload Form
This component has a Form to upload new File. It calls FileUploadService.pushFileToStorage()
method.
components/upload-form/upload-form.component.ts
import { Component, OnInit } from '@angular/core';
import { FileUploadService } from 'src/app/services/file-upload.service';
import { FileUpload } from 'src/app/models/file-upload.model';
@Component({
selector: 'app-upload-form',
templateUrl: './upload-form.component.html',
styleUrls: ['./upload-form.component.css']
})
export class UploadFormComponent implements OnInit {
selectedFiles: FileList;
currentFileUpload: FileUpload;
percentage: number;
constructor(private uploadService: FileUploadService) { }
ngOnInit(): void {
}
selectFile(event): void {
this.selectedFiles = event.target.files;
}
upload(): void {
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);
}
);
}
}
components/upload-form/upload-form.component.html
<label>
<input
type="file"
class="text-nowrap text-truncate"
(change)="selectFile($event)"
/>
</label>
<button
class="btn btn-success btn-sm"
[disabled]="!selectedFiles"
(click)="upload()"
>
Upload
</button>
<div *ngIf="currentFileUpload" class="progress mt-2">
<div
class="progress-bar progress-bar-info"
role="progressbar"
attr.aria-valuenow="{{ percentage }}"
aria-valuemin="0"
aria-valuemax="100"
[ngStyle]="{ width: percentage + '%' }"
>
{{ percentage }}%
</div>
</div>
Component for File Details
This component will export item details for the list later. It hsa file name with url and a Delete button.
components/upload-details/upload-details.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { FileUploadService } from 'src/app/services/file-upload.service';
import { FileUpload } from 'src/app/models/file-upload.model';
@Component({
selector: 'app-upload-details',
templateUrl: './upload-details.component.html',
styleUrls: ['./upload-details.component.css']
})
export class UploadDetailsComponent implements OnInit {
@Input() fileUpload: FileUpload;
constructor(private uploadService: FileUploadService) { }
ngOnInit(): void {
}
deleteFileUpload(fileUpload): void {
this.uploadService.deleteFile(fileUpload);
}
}
components/upload-details/upload-details.component.html
<a href="{{ fileUpload.url }}">{{ fileUpload.name }}</a>
<button (click)="deleteFileUpload(fileUpload)"
class="btn btn-danger btn-sm" style="float: right">
Delete
</button>
Component for Displaying List of Files
This component calls FileUploadService.getFiles()
method.
components/upload-list/upload-list.component.ts
import { Component, OnInit } from '@angular/core';
import { FileUploadService } from 'src/app/services/file-upload.service';
import { map } from 'rxjs/operators';
@Component({
selector: 'app-upload-list',
templateUrl: './upload-list.component.html',
styleUrls: ['./upload-list.component.css']
})
export class UploadListComponent implements OnInit {
fileUploads: any[];
constructor(private uploadService: FileUploadService) { }
ngOnInit(): void {
this.uploadService.getFiles(6).snapshotChanges().pipe(
map(changes =>
// store the key
changes.map(c => ({ key: c.payload.key, ...c.payload.val() }))
)
).subscribe(fileUploads => {
this.fileUploads = fileUploads;
});
}
}
In the HTML template, we embed upload-details
component inside ngFor
.
components/upload-list/upload-list.component.html
<div class="card">
<div class="card-header">List of Files</div>
<ul
class="list-group list-group-flush"
*ngFor="let file of fileUploads"
>
<li class="list-group-item">
<app-upload-details [fileUpload]='file'></app-upload-details>
</li>
</ul>
</div>
Embed Components in App Component
app.component.html
<div class="container" style="width:600px">
<div>
<h3>bezkoder.com</h3>
<h4>{{ title }}</h4>
</div>
<div class="my-3">
<app-upload-form></app-upload-form>
</div>
<div class="my-2">
<app-upload-list></app-upload-list>
</div>
</div>
Run & Check
You can run this App with command: ng serve
.
Open browser with url: http://localhost:4200/
and check the result.
Conclusion
Today we’ve built an Angular 10 File Upload Application successfully working with Firebase Storage using AngularFireStorage
from @angular/fire
library. Now we can upload, display, delete files at ease.
You can find how to make CRUD operations with Realtime DB or Firestore in the posts:
– Angular 10 Firebase CRUD Realtime DB with AngularFireDatabase
– Angular 10 Firestore CRUD with AngularFireStore
Or create Angular HTTP Client for working with Restful API in:
Angular 10 CRUD Application example with Web API
If you want to implement Reactive Form Validation, please visit:
Angular 10 Form Validation example (Reactive Forms)
Happy learning, see you again!
Further Reading
- Angular Template Syntax
- Angular Router Guide
- https://www.npmjs.com/package/@angular/fire
- Firebase Web Get Started
- @angular/fire Firebase Storage docs
Fullstack CRUD Application:
- Angular 10 + Node.js Express + MySQL example
- Angular 10 + Node.js Express + MongoDB example
- Angular 10 + Node.js Express + PostgreSQL example
- Angular 10 + Spring Boot + MySQL example
- Angular 10 + Spring Boot + PostgreSQL example
- Angular 10 + Spring Boot + MongoDB example
- Angular 10 + Django example
Newer versions:
– Angular 11 File Upload with Firebase Storage
– Angular 12 File Upload with Firebase Storage
– Angular 13 File Upload with Firebase Storage
– Angular 14 File Upload with Firebase Storage
– Angular 15 File Upload with Firebase Storage
Source Code
You can find the complete source code for this tutorial on Github.
Excelent tutorial! It worked for me. But do you have the css that you used in the pictures? I like the style
Hi, it is Bootstrap 4, you can read the github code (index.html file).
Is it possibile to update the image on firebase storage and the relative informations on realtime database?
Is it possible to use firestore database instead of Realtime Storage for storing the reference of images
Hi, it is possible. You only need to change the way you write the images’ info.
For working with Firestore, kindly read this tutorial.
Best Angular and Firebase tutorial I have ever seen !