Ionic 7 Firebase File Upload with Progress Bar Tutorial

Last Updated on by in Firebase

If you are a novice developer and want to see how Ionic and Firebase work together, this tutorial is for you.

In this tutorial, we will learn how to create a file uploading service in Ionic and store the file/image in Firebase storage.

You will also learn how to display the file/image uploading file uploading progress with a progress bar.

Firebase is a powerful, flexible and user-friendly database that takes care of almost every problem of yours.

Firebase development paradigm is one of the best; it has an innumerable number of features that make your life easy.

Such as File Storage, Authentication, Hosting, Testing, and many more.

Not just that, but you can also develop highly scalable apps with the following features:

  • Email & password, Google, Facebook, and Github authentication
  • Ready-made API
  • Realtime data
  • Robust security
  • Static file hosting
  • File storage supported by Google Cloud Storage

We will create an Ionic project and precisely target image uploading functionality along with progress indicator. So, let’s get along:

Set up Ionic Environment

This tutorial assumes that you have already updated to minimum Node 18, and Ionic 7. Make sure to update your development environment as per the mentioned requirement.

First, install Ionic CLI executing the following command:

sudo npm install -g @ionic/cli

Verify Ionic CLI installation:

ionic --version

Start creating a brand new Ionic project:

ionic start ionic-firebase-file-upload blank --type=angular

Get inside the project:

cd ionic-firebase-file-upload

Run the below command to add the native core package.

npm install @ionic-native/core --legacy-peer-deps

Disable Strict Type Errors

To avoid TypeScript compiling issues, we just need to open tsconfig.json file:

First, set below property under “compilerOptions” property.

"compilerOptions": {
    "strictPropertyInitialization": false,
    "skipLibCheck": true
    ...
}

Secondly, add given props under “angularCompilerOptions”.

"angularCompilerOptions": {
    "strictTemplates": false,
    ...
}

Create Firebase Project + Database Set Up

Head over to console.firebase.google.com, login using email then click on Add project.

Setting up Firebase Project

Declare the project name and click on continue.

Ionic 6 Firebase Auth Tutorial

Next, click on the denoted icon.

Ionic Firebase Auth Tutorial

Next up, provide the App nickname.

Ionic Firebase App Nickname

Copy the Firebase credentials that you are seeing on the screen, keep the details in a notepad.

We’ll need these credentials later to establish the consensus between Ionic Angular and Firebase database.

Firebase Config Keys

Then, click on the “Firestore” menu item.

Configure the database rules and set it “test mode”.

Configure Firebase Storage

We might get the permission denied error, to fix this issue, click on the storage -> Rules and replace the existing security rules with the following rule and then click on the publish button.

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth == null;
    }
  }
}

Register firebase credentials into the Ionic Angular environment file. Add the following code in environment.ts file.

// environments/environment.ts

export const environment = {
  production: false,
  firebaseConfig: {
    apiKey: "xxxxxx-xxxxxx_xxxxxxxxxxxxxxxxxx",
    authDomain: "xxxxxx_xxxxxxxxxxxxxxxxxx",
    databaseURL: "xxxxxx_xxxxxxxxxxxxxxxxxx",
    projectId: "xxxxxx_xxxxxxxxxxxxxxxxxx",
    storageBucket: "xxxxxx_xxxxxxxxxxxxxxxxxx",
    messagingSenderId: "xxxxxxxxxxxxxxxxxx",
    appId: "1:xxxxxxxxxxxxxxxxxx:web:xxxxxxxxxxxxxxxxxx"
  }
};

Install and Configure Angular Firebase Package in Ionic

Execute the command to install the official Angular library for Firebase.

npm install @angular/fire firebase@9.16.0 --legacy-peer-deps

Next, register the AngularFire package in Ionic’s main app module.

Add the following code in app.module.ts file:

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

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

// Firebase + environment
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFireStorageModule } from '@angular/fire/compat/storage';
import { AngularFirestoreModule } from '@angular/fire/compat/firestore';
import { environment } from '../environments/environment';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    IonicModule.forRoot(),
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebaseConfig),
    AngularFirestoreModule,
    AngularFireStorageModule,
  ],
  providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
  bootstrap: [AppComponent],
})

export class AppModule {}

Build Image Uploading Feature

Now every thing has been configured, let’s start building Ionic/Angular image uploading functionality with Firebase.

We are using the default home component to integrate the file uploading feature, so add the following code in the home.page.ts file.

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

import { Observable } from 'rxjs';
import { finalize, tap } from 'rxjs/operators';
import {
  AngularFireStorage,
  AngularFireUploadTask,
} from '@angular/fire/compat/storage';
import {
  AngularFirestore,
  AngularFirestoreCollection,
} from '@angular/fire/compat/firestore';

export interface imgFile {
  name: string;
  filepath: string;
  size: number;
}

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  // File upload task
  fileUploadTask: AngularFireUploadTask;

  // Upload progress
  percentageVal: Observable<any>;

  // Track file uploading with snapshot
  trackSnapshot: Observable<any>;

  // Uploaded File URL
  UploadedImageURL: Observable<string>;

  // Uploaded image collection
  files: Observable<imgFile[]>;

  // Image specifications
  imgName: string;
  imgSize: number;

  // File uploading status
  isFileUploading: boolean;
  isFileUploaded: boolean;

  private filesCollection: AngularFirestoreCollection<imgFile>;

  constructor(
    private afs: AngularFirestore,
    private afStorage: AngularFireStorage
  ) {
    this.isFileUploading = false;
    this.isFileUploaded = false;

    // Define uploaded files collection
    this.filesCollection = afs.collection<imgFile>('imagesCollection');
    this.files = this.filesCollection.valueChanges();
  }

  uploadImage(event: FileList) {
    const file: any = event.item(0);

    // Image validation
    if (file.type.split('/')[0] !== 'image') {
      console.log('File type is not supported!');
      return;
    }

    this.isFileUploading = true;
    this.isFileUploaded = false;

    this.imgName = file.name;

    // Storage path
    const fileStoragePath = `filesStorage/${new Date().getTime()}_${file.name}`;

    // Image reference
    const imageRef = this.afStorage.ref(fileStoragePath);

    // File upload task
    this.fileUploadTask = this.afStorage.upload(fileStoragePath, file);

    // Show uploading progress
    this.percentageVal = this.fileUploadTask.percentageChanges();
    this.trackSnapshot = this.fileUploadTask.snapshotChanges().pipe(
      finalize(() => {
        // Retreive uploaded image storage path
        this.UploadedImageURL = imageRef.getDownloadURL();

        this.UploadedImageURL.subscribe(
          (resp) => {
            this.storeFilesFirebase({
              name: file.name,
              filepath: resp,
              size: this.imgSize,
            });
            this.isFileUploading = false;
            this.isFileUploaded = true;
          },
          (error) => {
            console.log(error);
          }
        );
      }),
      tap((snap: any) => {
        this.imgSize = snap.totalBytes;
      })
    );
  }

  storeFilesFirebase(image: imgFile) {
    const fileId = this.afs.createId();

    this.filesCollection
      .doc(fileId)
      .set(image)
      .then((res) => {
        console.log(res);
      })
      .catch((err) => {
        console.log(err);
      });
  }
}

Build Custom File Size Pipe Filter

Next, we need to create the custom pipe filter, it will transform file size data and make it readable. Create home/format-file-size.pipe.ts file within the home directory.

import {Pipe, PipeTransform} from '@angular/core';

const FILE_SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const FILE_SIZE_UNITS_LONG = ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Pettabytes', 'Exabytes', 'Zettabytes', 'Yottabytes'];

@Pipe({
  name: 'formatFileSize'
})

export class FormatFileSizePipe implements PipeTransform {
  transform(sizeInBytes: number, longForm: boolean): string {
    const units = longForm
      ? FILE_SIZE_UNITS_LONG
      : FILE_SIZE_UNITS;
    let power = Math.round(Math.log(sizeInBytes)/Math.log(1024));
  	power = Math.min(power, units.length - 1);
  	const size = sizeInBytes / Math.pow(1024, power); // size in new units
  	const formattedSize = Math.round(size * 100) / 100; // keep up to 2 decimals
  	const unit = units[power];
  	return `${formattedSize} ${unit}`;
  }
}

Next, import and register the FormatFileSizePipe custom pipe filter in home.module.ts file.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { FormsModule } from '@angular/forms';
import { HomePage } from './home.page';

import { HomePageRoutingModule } from './home-routing.module';

import { FormatFileSizePipe } from './format-file-size.pipe';


@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    IonicModule,
    HomePageRoutingModule
  ],
  declarations: [
    HomePage,
    FormatFileSizePipe
  ]
})

export class HomePageModule {}

Create File Upload aButton

This step tells you how to create File uploading button and bind the functions to upload and store file in cloud firestore storage.

Add the following code in home.page.html file.

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title> Ionic Firebase File Upload Demo </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-card class="ion-text-center" *ngIf="!isFileUploading && !isFileUploaded">
    <ion-card-header>
      <ion-card-title>Choose Images to Upload</ion-card-title>
    </ion-card-header>
    <ion-card-content>
      <ion-button color="primary" size="medium">
        <input type="file" (change)="uploadImage($event.target.files)" />
      </ion-button>
    </ion-card-content>
  </ion-card>

  <!-- File upload progress bar -->
  <div *ngIf="percentageVal | async as percentage">
    Progress: {{ percentage | number }}%
    <ion-progress-bar value="{{ percentage / 100 }}"></ion-progress-bar>
  </div>

  <div *ngIf="trackSnapshot | async as snap">
    File size: {{ snap.totalBytes | formatFileSize }} Data transfered: {{
    snap.bytesTransferred | formatFileSize }}
  </div>
</ion-content>

Start Project

Eventually, we need to test the Ionic image uploading feature. So, run the following command and start the application.

npm install --save-dev @ionic/lab

Open the app:

ionic serve -l

The final output:

Ionic 6 Firebase File/Image Upload with Progress Bar Example

Summary

In this tutorial we have learned how to create Image uploading with progress bar functionality in Ionic application.

We have also seen how to easily store the images in Firebase storage and display file uploading progress with progress bar component.