Upload Files from Ionic Angular to Firebase Storage.
Wall Script
MailxEngine
Saturday, December 19, 2020

Upload Files from Ionic Angular to Firebase Storage.

Nowadays Google Firebase is my most favorite application. This is offering great web solutions like hosting, authentication, storage and database in a simple way. This article explains how to upload images(supports video) into Firebase storage with Ionic and Angular applications. This covers the user authentication part to protect storage uploads and improving default Firebase security rules. Take a look at the quick demo and try to upload under 1 mb JPEG or PNG.

Publish an Ionic Android App to Google Play Store


Live Demo

Video Tutorial


GitHub Source

System Requirements
  • Node JS
  • Angular Cli
  • Ionic Cli

Install Ionic
$npm install -g ionic

Create Ionic Project
Choose Angular and tabs.
$ionic start ionicStorage --tabs

Install Firebase Plugins
We need to install Angular Fire modules
Change project directory
$cd ionicStorage

Install @angular/fire
$npm install @angular/fire

Install Firebase - Need this for social provider options.
$npm install firebase

Create Components
Create a new login page component for user authentication.
$ng generate component login

Generate Login Module for lazy loading.
$ng generate module login

login-routing.module.ts Create a routing module for login component.
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './login.component';

const routes: Routes = [
{
path: '',
component: LoginComponent,
},
];

@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class LoginRoutingModule {}

login.module.ts
Import the login component here.
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { IonicModule } from '@ionic/angular';
import { LoginRoutingModule } from './login-routing.module';
import { LoginComponent } from './login.component';

@NgModule({
declarations: [LoginComponent],
imports: [CommonModule, IonicModule, LoginRoutingModule],
})
export class LoginModule {}


app-routing.module.ts
Introduce new route path for login page.
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';

const routes: Routes = [
{
    path: '',
    loadChildren: () =>
    import('./tabs/tabs.module').then((m) => m.TabsPageModule)
},
{
    path: 'login',
    loadChildren: () =>
    import('./login/login.module').then((m) => m.LoginModule)
},
];
@NgModule({
imports: [
RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }),
],
exports: [RouterModule],
})
export class AppRoutingModule {}


Launch Ionic Project
Launch the application and validate https://localhost:8100/login
$ionic serve


Getting started with Firebase
Google Firebase is a web application, using this you can solve complex problems..
Create a Firebase application
Select web for configuration details.
Firebase setup

Application name.
Firebase setup

Firebase configuration details.
Firebase setup

Create a config directory and create a config file.
Firebase setup


Firebase Config
Constants file for Firebase application keys.
export const firebaseConfig = {
    apiKey: 'api key',
    authDomain: "domain address",
    databaseURL: "database URL",
    storageBucket: "storage bucket us",
    messagingSenderId: "Message Sender ID",
    appId: "#######"
};


Firebase Authentication
You will find this option is sidebar menu.
Firebase authentication

Enable Google Login
Create an application for Google OAuth.
Firebase authentication

Make sure Google status should be enabled.
Firebase authentication

app.module.ts
Import Angular fire auth module and storage module. Initiliza with AngularFirebaseModule
import { NgModule } from '@angular/core';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { firebaseConfig } from './config/firebase.config';
import { AngularFireStorageModule } from '@angular/fire/storage';

@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [
    BrowserModule,
    IonicModule.forRoot(),
    AppRoutingModule,
    AngularFireModule.initializeApp(firebaseConfig),
    AngularFireAuthModule,
    AngularFireStorageModule
],
providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
],
bootstrap: [AppComponent],
})
export class AppModule {}

 

Firebase Auth Service
Create a service for Firebase social authentication
$ng generate service services/firebaseAuth

Firebase Auth Service
Here method firebaseSocialLogin deals with Firebase API and storing the user response into browser local storage.

import { Injectable } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { Router } from '@angular/router';
import { firebase } from '@firebase/app';
import '@firebase/auth';

@Injectable({
providedIn: 'root',
})
export class FirebaseAuthService {
constructor(private angularFireAuth: AngularFireAuth, public router: Router) {}

firebaseSocialLogin(provider) {
    this.angularFireAuth.signInWithPopup(provider).then((res: any) => {
    localStorage.setItem('user', JSON.stringify(res.user));
    this.router.navigate(['']);
    });
}

googleLogin() {
    const provider = new firebase.auth.GoogleAuthProvider()
    return this.firebaseSocialLogin(provider);
}

getUser(){
    const userData = localStorage.getItem('user');
    return JSON.parse(userData);
}

logout() {
    this.angularFireAuth.signOut();
    localStorage.setItem('user', null);
    localStorage.removeItem('user');
    this.router.navigate(['login']);
}
}



Guards
Create guards for protecting application routes from accessing directly.

auth.guard.ts
This guard product the home(tabs) route, only authenticated users can upload images.
import { FirebaseAuthService } from './../services/firebase-auth.service';
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';

@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(public firebaseAuthService: FirebaseAuthService, public router: Router) {}

canActivate(): boolean {
    if (!this.firebaseAuthService.getUser()) {
        this.router.navigate(['login']);
        return false;
    }
    return true;
}
}

login.guard.ts
Opposite to auth.guard, it validates the user data in local storage.
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { FirebaseAuthService } from '../services/firebase-auth.service';


@Injectable({
providedIn: 'root'
})
export class LoginGuard implements CanActivate {
constructor(public firebaseAuthService: FirebaseAuthService, public router: Router) {}

canActivate(): boolean {
    if (this.firebaseAuthService.getUser()) {
        this.router.navigate(['']);
        return false;
    }
    return true;
}
}

app-routing.module.ts
Connect with guards a new route for login page. Include routing hashing for production deployments useHash:true
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './guards/auth.guard';
import { LoginGuard } from './guards/login.guard';

const routes: Routes = [
{
    path: '',
    loadChildren: () =>
    import('./tabs/tabs.module').then((m) => m.TabsPageModule),
    canActivate: [AuthGuard],
},
{
    path: 'login',
    loadChildren: () =>
    import('./login/login.module').then((m) => m.LoginModule),
    canActivate: [LoginGuard],
},
];
@NgModule({
imports: [
RouterModule.forRoot(routes, { useHash: true, preloadingStrategy: PreloadAllModules }),
],
exports: [RouterModule],
})
export class AppRoutingModule {}


login.component.html
Connected with Google social login button.
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
Firebase Storage
</ion-title>
</ion-toolbar>
</ion-header>

<ion-content>
<ion-card>
<ion-card-header>
<ion-card-title>Welcome to Firebase Storage</ion-card-title>
</ion-card-header>
<ion-card-content>
<ion-button color="primary" expand="block" (click)="firebaseAuthService.googleLogin()">Google Login</ion-button>
</ion-card-content>
</ion-card>
</ion-content>

Child Components
Create a components directory under application source(src).

Components
Generate a components module for lazy loading integration.
$ng generate module components

Photo Upload Component
Generate a photo upload component.
$ng generate component components/photoUpload

Photos Preview List
Generate a photos list preview component.
$ng generate component components/photosList

components.module.ts
Export child components here.
import { IonicModule } from '@ionic/angular';
import { PhotosListComponent } from './photos-list/photos-list.component';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { PhotoUploadComponent } from './photo-upload/photo-upload.component';

@NgModule({
declarations: [PhotosListComponent, PhotoUploadComponent],
exports: [PhotosListComponent, PhotoUploadComponent],
imports: [CommonModule, IonicModule],
})
export class ComponentsModule {}


tab1.module.ts
Import components module for accessing all of the child components .
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { ComponentsModule } from './../components/components.module';
import { Tab1PageRoutingModule } from './tab1-routing.module';
import { Tab1Page } from './tab1.page';

@NgModule({
imports: [
    IonicModule,
    CommonModule,
    FormsModule,
    ComponentsModule,
    Tab1PageRoutingModule,
],
declarations: [Tab1Page],
})
export class Tab1PageModule {}


tab1.page.ts
On page load calling the getUser and assigning to userProfileData.
import { Component, OnInit } from '@angular/core';
import { FirebaseAuthService } from './../services/firebase-auth.service';

@Component({
selector: 'app-tab1',
templateUrl: 'tab1.page.html',
styleUrls: ['tab1.page.scss'],
})
export class Tab1Page implements OnInit {
userProfileData: any;
constructor(private firebaseAuthService: FirebaseAuthService) {}
ngOnInit() {
    this.userProfileData = this.firebaseAuthService.getUser();
}

logoutAction() {
    this.firebaseAuthService.logout();
}
}


tab1.page.html
Display user data and include app-photo-upload component.
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
    Photo Upload
</ion-title>
<ion-button color="primary " slot="end" (click)="logoutAction()" >Log out</ion-button>
</ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Photo Upload</ion-title>
</ion-toolbar>
</ion-header>

<ion-card>
<ion-card-header>
<ion-card-title>Welcome to {{userProfileData?.displayName}}</ion-card-title>
</ion-card-header>
<ion-card-content>
<app-photo-upload></app-photo-upload>
</ion-card-content>
</ion-card>
</ion-content>

Firebase Storage

Click on storage and setup.
Firebase storage

Storage default security rules
Firebase storage

Choose the storage region.
Firebase storage

Create an uploads folder.
Firebase storage


Firebase upload service
Generate upload service for uploading files.
$ng generate service services/firebaseUpload

firebase-upload.service.ts
Here storeImage is a promise method and this will return with Firebase file download URL.
import { Injectable } from '@angular/core';
import { AngularFireStorage } from '@angular/fire/storage';

@Injectable({
    providedIn: 'root',
})
export class FirebaseUploadService {
location = 'uploads/';
constructor(private angularFireStorage: AngularFireStorage) {}

/* Image name generator time */
imageName() {
    const newTime = Math.floor(Date.now() / 1000);
    return Math.floor(Math.random() * 20) + newTime;
}

async storeImage(imageData: any) {
try {
    const imageName = this.imageName();
    return new Promise((resolve, reject) => {
    const pictureRef = this.angularFireStorage.ref(this.location + imageName);
    pictureRef
    .put(imageData)
    .then(function () {
    pictureRef.getDownloadURL().subscribe((url: any) => {
    resolve(url);
    });
})
.catch((error) => {
    reject(error);
});
});
} catch (e) {}
}
}


photo-upload.component.ts
Image upload component sends the file data to storeImage method.
import { Component, OnInit } from '@angular/core';
import { FirebaseUploadService } from './../../services/firebase-upload.service';

@Component({
selector: 'app-photo-upload',
templateUrl: './photo-upload.component.html',
styleUrls: ['./photo-upload.component.scss'],
})
export class PhotoUploadComponent implements OnInit {
barStatus = false;
imageUploads = [];
constructor(private firebaseUploadService: FirebaseUploadService) {}

ngOnInit() {}

// Upload image action
uploadPhoto(event) {
this.barStatus = true;
this.firebaseUploadService.storeImage(event.target.files[0]).then(
    (res: any) => {
        if (res) {
            console.log(res);
            this.imageUploads.unshift(res);
            this.barStatus = false;
    }
},
(error: any) => {
    this.barStatus = false;
}
);
}
}


photo-upload.component.html
User input type file for uploading files. You can change the extension if you are working with videos or documents.
<div>
<form method="post" enctype="multipart/form-data">
<div>
<div><b>Upload photo</b></div><br/>
<input type="file" (change)="uploadPhoto($event)" accept=".png,.jpg" multiple="true" />
</div>
</form>
</div>
<div *ngIf="barStatus">
Uploading.....
</div>

<app-photos-list [imageUploads]="imageUploads"></app-photos-list>

photos-list.component.ts
Here imageUploads is an input atribute.
import { Component, Input } from '@angular/core';

@Component({
selector: 'app-photos-list',
templateUrl: './photos-list.component.html',
styleUrls: ['./photos-list.component.scss'],
})
export class PhotosListComponent {
@Input() imageUploads: any;
constructor() {}
}


photos-list.component.html
Use *ngFor and display.
<div id="photoPreview">
    <div *ngFor="let image of imageUploads">
        <img loading="lazy" [src]="image" class="preview" />
    </div>
</div>


Firebase Storage Security Rules
Wathc the video demos for better understanding.
rules_version = '2';
service firebase.storage {
  match /b/ionicfirebasestorage-c1d6d.appspot.com/o {
    match /uploads/{imageId} {
      allow write: if request.auth != null &&  request.resource.size < 1 * 1024 * 1024
                         && request.resource.contentType.matches('image/.*');
    }
    match /uploads/{imageId} {
      allow read: if request.auth != null;
    } 
  }
}

web notification

0 comments:

Make in India