Upload files in Ionic with AngularFire2

Jeff Delaney

The cloud is your hard drive, but are you prepared to deal with file validation, network latency, scalability, and security? I know I’m not. Luckily, developers now have access to AngularFire2 Storage, which blends Firebase Cloud Storage seamlessly into an Ionic project.

We’ll demonstrate how to capture an image files with the Ionic native camera plugin, then manage an upload task. The task object exposes a handful of methods that can be used to build a UX capable of monitoring and interacting with the upload.

Getting Started

Let’s start from a blank Ionic app, then install AngularFire2 and the native Camera plugin.

# start an ionic project
$ ionic start fireUpload blank
$ cd fireUpload

# install firebase packages
$ npm install angularfire2 firebase --save

# install native camera
$ ionic cordova plugin add cordova-plugin-camera
$ npm install --save @ionic-native/camera

Import the required AngularFire modules and add the camera to the providers array. Don’t forget to include your Firebase project credentials.

app.module.ts

// ...omitted
import { AngularFireModule } from 'angularfire2';
import { AngularFireStorageModule } from 'angularfire2/storage';

import { Camera } from '@ionic-native/camera';

const firebase = {
 // your firebase web config
}

@NgModule({
  declarations: [
    MyApp,
    HomePage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    AngularFireModule.initializeApp(firebase),
    AngularFireStorageModule
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    Camera
  ]
})
export class AppModule {}

Building an Upload Component

The next step is to generate a component to handle the upload logic.

$ ionic generate component upload

Inside the component, we need to inject our dependencies in the constructor, then declare three important properties:

  1. task is the main upload object.
  2. progress is an Observable number ranging from 0 to 100 that starts emitting values when the task is created.
  3. image is a base64 string returned by the native camera plugin.

upload.ts


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

import { AngularFireStorage, AngularFireUploadTask } from 'angularfire2/storage';

import { Camera, CameraOptions } from '@ionic-native/camera';

@Component({
  selector: 'upload',
  templateUrl: 'upload.html',
})
export class UploadComponent {

  task: AngularFireUploadTask;

  progress: any;  // Observable 0 to 100

  image: string; // base64

  constructor(public storage: AngularFireStorage,
              private camera: Camera) { }

  // Our methods will go here...

}

Capture with the Camera

Now that we have all the pieces in place, let’s start by capturing an image from a native mobile camera. The camera takes several configuration options - the important one being DestinationType.DATA_URL, which tells the camera to return a base64 encoded image.

upload.ts


 async captureImage() {
    const options: CameraOptions = {
      quality: 100,
      destinationType: this.camera.DestinationType.DATA_URL,
      encodingType: this.camera.EncodingType.JPEG,
      mediaType: this.camera.MediaType.PICTURE,
      sourceType: this.camera.PictureSourceType.CAMERA
    }

    return await this.camera.getPicture(options)
}

Upload with AngularFire

There are two main inputs required when uploading to Firebase Storage - the file path, and the file itself. Because we’re dealing with a base64 image, we use putString(str, 'file_url') to create the upload task. Once created it will start uploading immediately (it’s not an Observable, so no need to subscribe).

All files in a Firebase storage bucket must have a unique path. Adding a timestamp to each file name is an easy way to avoid collisions.


createUploadTask(file: string): void {

    const filePath = `my-pet-crocodile_${ new Date().getTime() }.jpg`;

    this.image = 'data:image/jpg;base64,' + file;
    this.task = this.storage.ref(filePath).putString(this.image, 'data_url');

    this.progress = this.task.percentageChanges();
}

async uploadHandler() {
   const base64 = await this.captureImage();
   this.createUploadTask(base64);
}

Simply bind the handler to a button and you’re ready to upload.

<img *ngIf="image" [src]="image">

<button ion-button (tap)="uploadHandler()">
    Upload!
</button>

Monitor Upload Progress

Your users probably want to monitor the progress of an upload and AngularFire has anticipated this requirement. In the previous step we defined a progress Observable, which will emit a number between 0 and 100 several times per second during the upload - exactly what we need for a progress bar.

<ng-container *ngIf="progress | async as percent">

  <progress [value]="percent" max="100"></progress>

</ng-container>

Pause and Resume the Upload Task

If your app handles large file sizes, you may want to give users fine-grained control over the upload process. The task object has pause and resume methods that can be used as button click event handlers.

<button ion-button (tap)="task.pause()">
    Pause
</button>

<button ion-button (tap)="task.resume()">
    Resume
</button>

👉 And that's all it takes to transmit files from a camera to a permanent cloud storage bucket. AngularFire2 Storage has a few more tricks up its sleeve, so make sure to check out the official docs.

  Tweet It

🕵 Search Results

🔎 Searching...