Using Firebase Storage in Ionic
Most applications stock information so user's can leave and come back later.
In the past we only had one option: stocking the data in a separate folder on the server side.
In this tutorial we explore the new way to easily stock our data using Firebase's Storage Service and skip all the DevOps rules.
We are going to recreate one of the Ionic/AWS stack feature: taking a picture, uploading it to the third tier service and displaying it. A previous tutorial covered this case for this technology so you can have a look there if you want another perspective.
When hosting files on a private server, it's wiser to restrict access to those files in order to prevent hackers from accessing important files. Since we are using Firebase, Google's engineers already handled this part and users already have a limited access to the system so they can't mess up our system.
Some rules can still be set in the Storage section of the project, by default a user has to be authenticated to access the files.
We disable those rules because we won't create this authentication feature:
Our first goal: accessing a file.
Basic file access
We start by uploading a file for test purpose in the web interface:
Then set up our Ionic project followed by Ionic & Cordova plugins installation:
ionic start ionic-firebase-storage blank
cd ionic-firebase-storage
npm install firebase promise-polyfill @ionic-native/camera --save
ionic cordova plugin add cordova-plugin-camera
The home.html is pretty simple:
<ion-header>
<ion-navbar>
<ion-title>
Ionic Firebase Storage
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<div padding text-center>
<a href="{{someTextUrl}}">Get some text there</a>
</div>
</ion-content>
A link will allow us to download the file we already uploaded.
However, the url can't be static. A property named someTextUrl will acquire this url in the home.ts file. Before diving in this file, we need to acquire the Firebase configuration and add it to our project.
This configuration is located in the src/environments/environment.ts file:
export const environment = {
production: false,
firebase: {
apiKey: " AIzaSyAN4H-9VmncDG0PfqmuhmHrXuBtvCojT2E",
authDomain: "todo-firebase-c4298.firebaseapp.com",
storageBucket: "todo-firebase-c4298.appspot.com",
projectId: "todo-firebase-c4298"
}
};
Those information come from the Authentication section:
Now the home.ts file:
import * as firebase from 'firebase';
import { environment } from '../../environments/environment';
...
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
someTextUrl;
constructor() {
firebase.initializeApp(environment.firebase);
this.getSomeText();
}
getSomeText() {
firebase.storage().ref().child('some text').getDownloadURL()
.then(response => this.someTextUrl = response)
.catch(error => console.log('error', error))
}
}
Everything from the Firebase library is imported in a firebase variable and the someTextUrl property is declared for later use.
In the constructor, Firebase is initialized using our configuration file and the getSomeText method is called.
This is where the firebase variable is used for the first time.
The firebase variable contains everything from Firebase, however, we only need the Storage service so we call the storage method and acquire a Reference to the root.
Firebase is path-oriented, from the root we acquire a child file named "some text" and ask for the url. Once we got it we can set our someTextUrl property and test our link:
Great! We have access to our files.
Now let's create the photo upload feature!
Photo Upload
We already installed Ionic and Cordova plugins, so we add them to the app.module.ts file:
import {
IonicApp,
IonicErrorHandler,
IonicModule,
LoadingController } from 'ionic-angular';
import { Camera } from '@ionic-native/camera';
...
@NgModule({
...
providers: [
...
Camera,
LoadingController
]
})
export class AppModule {}
We add two new Providers here:
- Camera: To grab a picture
- LoadingController: To display a loader during the whole process
Then updating the home.html file:
<ion-content padding>
<div padding text-center>
<img src={{currentImage}} />
<button ion-button clear (click)="grabPicture()">Take a photo</button>
</div>
</ion-content>
We have two Elements now.
A button that will trigger the grabPicture method. This method will display the camera, upload the data to Firebase once the picture is taken and update a property named currentImage with Firebase's file url.
The img tag will then display the picture directly from Firebase.
Before diving in the home.ts file you need to know that the camera feature is also available in your browser.
You only need to install the browser platform:
ionic cordova platform add browser
And run the platform:
ionic cordova run browser
The home.ts initialization changed a bit:
import { Component } from '@angular/core';
import { NavController, LoadingController } from 'ionic-angular';
import * as firebase from 'firebase';
import { Camera, CameraOptions } from '@ionic-native/camera';
import { environment } from '../../environments/environment';
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
someTextUrl;
selectedPhoto;
loading;
currentImage;
constructor(public navCtrl: NavController, public camera: Camera,
public loadingCtrl: LoadingController) {
firebase.initializeApp(environment.firebase);
}
}
Two news imports, the same as the app.module.ts file. LoadingController in a loadingCtrl property and Camera in a camera property.
The CameraOptions will be used later to format the image's data.
Property wise, the selectedPhoto will contain the data from the camera, loading the new loader and currentImage the image to display.
We add the grabPicture method:
grabPicture() {
const options: CameraOptions = {
quality: 100,
targetHeight: 200,
targetWidth: 200,
destinationType: this.camera.DestinationType.DATA_URL,
encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE
}
this.camera.getPicture(options).then((imageData) => {
this.loading = this.loadingCtrl.create({
content: 'Please wait...'
});
this.loading.present();
this.selectedPhoto = this.dataURItoBlob('data:image/jpeg;base64,' + imageData);
this.upload();
}, (err) => {
console.log('error', err);
});
}
Using the default CameraOptions from the Ionic documentation we trigger the getPicture method from the camera property.
A new loader is created then shown.
The data we received from the camera are then transformed into a Blob so we can properly stock it in Firebase with the following method:
dataURItoBlob(dataURI) {
// codej adapted from:
// http://stackoverflow.com/questions/33486352/
//cant-upload-image-to-aws-s3-from-ionic-camera
let binary = atob(dataURI.split(',')[1]);
let array = [];
for (let i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
};
Finally, the following upload method is called:
upload() {
if (this.selectedPhoto) {
var uploadTask = firebase.storage().ref().child('images/uploaded.png')
.put(this.selectedPhoto);
uploadTask.then(this.onSuccess, this.onError);
}
}
If we have the photo information, a new uploadTask is created.
This task will acquire Firebase’s root reference then point to the images/uploaded.png path and create this file using the selectedPhoto data.
We then listen to the success or error:
onSuccess = snapshot => {
this.currentImage = snapshot.downloadURL;
this.loading.dismiss();
};
onError = error => {
console.log("error", error);
this.loading.dismiss();
};
A snapshot is retrieved on success, it is an immutable copy of the data from Firebase. We acquire the downloadUrl from the snapshot and stock it in the currentImage property.
If there is an error, the error is logged.
In both cases, the loader is dismissed.
Our final result:
Conclusion
Delegating file storage is easy, quick and cost efficient for small projects.
We only need a Firebase project and the Firebase JavaScript library. The library is initialized using the API keys located in Firebase’s web interface.
Firebase’s root is used as a reference, then we move to the folder or file we want to target, in this tutorial we used the getDownloadUrl method to acquire a file’s url according to it’s path in the tree and the put method to upload a new file.
If you liked this tutorial, you can learn more on Firebase’s cloud functions, database, twitter authentication and basic authentication.