Ionic 3 Image Upload to Firebase and Cropper JS

Ionic 3 Image Upload to Firebase and Cropper JS

Easy Image Uploads with Firebase

Introduction

Firebase has become the one-stop destination for newbie developers as it allows them to include complex functionalities into their application with little or no background knowledge. Furthermore it's free tier is sufficient enough for most starter projects.

This post will discuss about one specific feature of firebase, Image Upload.

Firebase Initial Setup

Before we get started, make sure you have Firebase setup. Follow this link to finish the installation and setup process.

AngularFire2

Once we have completed the setup of AngularFire, lets dive into today's topic. Also make sure your firebase storage is set to public.

StackOverFlow

Firebase Image Upload

Once you have completed the above steps, make sure you have imported AngularFireStorageModule as well.

import { AngularFireModule } from '@angular/fire';
import { AngularFireStorageModule } from '@angular/fire/storage';
@NgModule({
  imports: [
    .............
    AngularFireModule.initializeApp(environment.firebase, 'my-app-name'), // imports firebase/app needed for everything
    AngularFireStorageModule // imports firebase/storage only needed for storage features
  ],
.........................

To follow best coding practices, make sure your providers and services are kept separate from your functional logic.

Let's create a new provider.

ionic g provider firebase

Now inside our provider, inject AngularFireStorage.

import { AngularFireStorage, AngularFireUploadTask } from '@angular/fire/storage';
....................
constructor(    private storage:AngularFireStorage  ) {  
  console.log('Hello FirebaseProvider Provider'); 

}

Now let's create a method.

uploadImageToStorage(base64Data):AngularFireUploadTask{    
//let path='THIS WILL BE YOUR IMAGE NAME AND PATH';
let path = 'Images/MyImage.jpg';
var imageRef = this.storage.ref(path); 
return imageRef.putString(base64Data, 'data_url');  
}

We can call this method anywhere in our app, and pass a base64 Image to it for upload to storage.

I used the camera plugin to get the image from the user. You can alternatively use the image picker plugin as well. Make sure to install them properly.

//import and inject the provider we created in the previous step
import { FirebaseProvider } from '../../../providers/firebase/firebase';
import { Camera,CameraOptions } from '@ionic-native/camera';
............
base64Image:string;// we will store our image in here
constructor(    
private camera:Camera,    
private firebaseService:FirebaseProvider,    
........................
  ) {    
console.log('Hello Medium Demo Component');      
}
..........
choosePhoto() {
    const options: CameraOptions = {      
quality: 50,//make sure to adjust the quality.Higher quality might crash your app     
destinationType: this.camera.DestinationType.DATA_URL,      encodingType: this.camera.EncodingType.JPEG,      
mediaType: this.camera.MediaType.PICTURE,      
sourceType: this.camera.PictureSourceType.PHOTOLIBRARY,      allowEdit:true    
}     
this.camera.getPicture(options).then((imageData) => {            // this.base64Image = 'data:image/jpg;base64,' + imageData;        
}, (err) => { 
     console.log(err)      // Handle error    
});  
}

Read the ionic docs for further information

Ionic Docs

By now, we have the Image Data ready for upload. Let's upload it now.

async updatePhoto(){    
try {      
let progressResult = this.firebaseService.uploadImageToStorage(this.base64Image);
      progressResult.percentageChanges()
.subscribe(        
res => {          
console.log(res);          
let progress=Math.floor(res);//You can get the image upload progress in here.        
});
let result = await progressResult;
let imageUrl = await result.ref.getDownloadURL();// the public access url of the image
let imagePath = result.ref.fullPath;// the storage path of the image
} catch(err){
 err => console.log(err)//handle error
}

Congratulations!!

You have successfully uploaded your image to firebase storage. You can check the uploaded image in your firebase console.

Cropper JS

Let's install cropperjs first

npm install cropperjs

CropperJS

Once the installation has been completed, let's create our own component for cropping images.

ionic g component image-cropper

Go to the image-cropper ts file and add the following,

import { Component, ViewChild, Input, Output, EventEmitter } from '@angular/core';
import { AngularCropperjsComponent } from 'angular-cropperjs'; 
...........
export class ImageCropperComponent {   
@ViewChild('angularCropper') public angularCropper: AngularCropperjsComponent;   
cropperOptions: any;   
@Input('imageSource')myImage;  
@Input('aspectRatio') aspectRatio:number;//input your own aspect ratio
@Output('croppedImage')croppedImage = new EventEmitter<any>();    croppedImageLocal;  
scaleValX = 1;  
scaleValY = 1;   
ngOnInit(){    
console.log(this.aspectRatio);    
this.cropperOptions = {      
dragMode: 'crop',      
aspectRatio: this.aspectRatio,      
autoCrop: true,      
movable: true,      
zoomable: true,      
scalable: true,      
autoCropArea: 0.8,    
};  
}   
reset() {    
this.angularCropper.cropper.reset();  
}   
clear() {    
this.angularCropper.cropper.clear();  
} 

rotate() {    
this.angularCropper.cropper.rotate(90);  
}   
zoom(zoomIn: boolean) {    
let factor = zoomIn ? 0.1 : -0.1;
this.angularCropper.cropper.zoom(factor);  
} 

scaleX() {    
this.scaleValX = this.scaleValX * -1;    this.angularCropper.cropper.scaleX(this.scaleValX);  
}  
scaleY() {    
this.scaleValY = this.scaleValY * -1;    this.angularCropper.cropper.scaleY(this.scaleValY);  
}   
move(x, y) {    
this.angularCropper.cropper.move(x, y);  
}   
save() {    
let croppedImgB64String: string = this.angularCropper.cropper.getCroppedCanvas().toDataURL('image/jpeg', (100 / 100));   

this.croppedImageLocal = croppedImgB64String;    this.croppedImage.emit(croppedImgB64String);    
} 

cropperTouchStart(event){    
event.stopPropagation();    
event.preventDefault(); //Most important  
}
}

Go to the image cropper HTML file and add the following,

<div *ngIf="myImage">
  <ion-row>
    <ion-col>
        <ion-buttons start>
            <button ion-button color="danger" (click)="reset()">
              Reset
            </button>
          </ion-buttons>
    </ion-col>
    <ion-col>
        <ion-buttons end>
            <button ion-button icon-only color="secondary" (click)="save()">
              <ion-icon name="checkmark"></ion-icon>
            </button>
          </ion-buttons>
    </ion-col>
  </ion-row>
  <angular-cropper #angularCropper (touchstart)="cropperTouchStart($event)" [cropperOptions]="cropperOptions" [imageUrl]="myImage" ></angular-cropper>
  <ion-row>
      <ion-col col-4>
        <button ion-button outline icon-left color="primary" (click)="zoom(true)">
          <ion-icon name="add"></ion-icon> Zoom
        </button>
      </ion-col>
      <ion-col col-4>
        <button ion-button outline icon-left color="primary" (click)="zoom(false)">
          <ion-icon name="remove"></ion-icon> Zoom
        </button>
      </ion-col>
      <ion-col col-4>
        <button ion-button outline icon-left (click)="rotate()">
          <ion-icon name="refresh"></ion-icon> 90 deg
        </button>
      </ion-col>

      <ion-col col-2>
        <button ion-button clear (click)="scaleX()">
          Flip X
        </button>
      </ion-col>
      <ion-col col-2>
        <button ion-button clear (click)="scaleY()">
          Flip Y
        </button>
      </ion-col>

      <ion-col col-2>
        <button ion-button clear icon-only (click)="move(0, -10)">
          <ion-icon name="arrow-round-up"></ion-icon>
        </button>
      </ion-col>
      <ion-col col-2>
        <button ion-button clear icon-only (click)="move(0, 10)">
          <ion-icon name="arrow-round-down"></ion-icon>
        </button>
      </ion-col>
      <ion-col col-2>
        <button ion-button clear icon-only (click)="move(-10, 0)">
          <ion-icon name="arrow-round-back"></ion-icon>
        </button>
      </ion-col>
      <ion-col col-2>
        <button ion-button clear icon-only (click)="move(10, 0)">
          <ion-icon name="arrow-round-forward"></ion-icon>
        </button>
      </ion-col>
    </ion-row>
    <img  [src]="croppedImageLocal" *ngIf="croppedImageLocal">
</div>

You can play around with the image cropper component and find which options suit you best. Read the documentation for further customization. Now to finish it off,

Make sure you import the Components Module to your page module which was created when creating the image cropper component.

imports: [
......................
ComponentsModule  
],

Now go to your HTML page and use the image cropper component,

<image-cropper *ngIf="base64Image" [imageSource]="base64Image" [aspectRatio]="aspectRatio" (croppedImage)="getCroppedImage($event)">
<!-- You can input your image source to be cropped and your desired aspect ratio. And receive the cropped image via the croppedImage event-->
</image-cropper>

1__YpOI8c8Y5aaR0YIKFmXrw.jpeg

Conclusion

We successfully uploaded an image to firebase using the Ionic 3 camera plugin. And we learned how to use the cropper JS library in an ionic component to make image editing easy. Hope you gained something from this. Ciao!!

Resources