File uploads come to AngularFire

Cloud Storage for Firebase is a serverless way to upload and download binary files straight from the browser. As of today, it’s officially supported by AngularFire. Adding AngularFire to your app allows you to easily and securely manage a Cloud Storage bucket. All without a line of server code.

Get ready, uploading files from your Angular app just got a lot easier.

AngularFire, meet Cloud Storage

If you’re not familiar with AngularFire, it’s the official Angular library for Firebase. AngularFire combines the power of Angular, Firebase, and RxJS to act as your serverless backend. It includes modules for the Realtime Database, Firebase Authentication, Cloud Firestore, and today it supports Cloud Storage.

Adding AngularFire to your project is easy. Install Firebase and AngularFire from npm:

npm i firebase angularfire2

Then add it to your NgModule:

Inject the AngularFireStorage module into your component.

Now you’re ready to manage your files from Cloud Storage without a server of your own.

The foundation of a file upload

The primary way of uploading files on the web is through the <input type="file"> tag.

<label for="file">File:</label>
<input type="file" (change)="upload($event)" accept=".png,.jpg" />

This tag fires a (change) event when the user selects a file. It even allows you to restrict the user from uploading undesired formats with the accept attribute (beware that this is only a client restriction, we’ll cover server restrictions later). Event bindings in Angular make it easy to handle a (change) event and send the file to Cloud Storage.

The sample above creates an AngularFireStorageReference with a randomly generated Id. This reference controls a path in your Cloud Storage bucket. Creating a reference doesn’t initiate an upload, but it allows to start one as well as delete the file saved at that location. Calling .put() on a reference with a Blob beings the upload to Cloud Storage.

You may have noticed that creating a reference and calling .put() is the longer way of calling afStorage.upload(path, blog) shown above. Under the hood the afStorage.upload() method creates a reference, calls .put(), and returns the AngularFireUploadTask for you.

The AngularFireUploadTask is how you’ll monitor the upload progress.

Reactive upload methods

The web platform provides an easy and accessible way of displaying the progress of any given task. This is done through the aptly named <progress> element.

<progress max="100" [value]="(uploadProgress | async)"></progress>

The progress element’s value attribute is easy to control with Angular’s property binding. AngularFire provides an upload observable that you can pipe in the new value as it changes.

The .snapshotChanges() method on an AngularFireUploadTask returns an object with helpful metadata about the upload. Properties such as the totalBytesTransferred, totalBytes in the upload, any metadata provided, the state of the upload, and the downloadURL of the file once uploaded. Using this metadata you can update the UI to show the progress.

Most of the time your UI will display the upload percentage and download url. Since this a common task, we made this a bit easier with two helpful methods: .percentageChanges() and .downloadURL().

Instead of calculating the changes yourself, the .percentageChanges() observable does the work for you. The .downloadURL() observable emits the download URL string once the upload is completed. This simplifies binding this information to your UI.

The best part of these observable methods is that you don’t need to worry about complex mapping or multiple .subscribe() calls in your component code. Just bind them to the async pipe and your users are watching their file upload progress.

But what if your user changes their mind? What if they want to pause their upload because they’re no longer on a WiFi network? Will they be able to resume when they want? What if they want to cancel the upload all together?

Controlling uploads

A user should be able to pause, resume, or cancel an upload in progress. If or when their upload completes they should be able to delete it as well.

An AngularFireUploadTask contains the following appropriately named methods: .pause().resume() , and .cancel() . In the sample above, the component stores the task as an instance property. This allows you to call it from your template:

Ideally you’ll want to update your UI so the user knows the state of their upload. The .snapshotChanges() observable emits this information through the state property.

When the user taps “Pause”, the 'paused' state emits from the observable. When the user taps “Resume”, the 'running' state emits. And of course, when the “Pause” button is tapped, the 'paused' state emits.

The template above will disable each button depending on the state. A user can cancel a 'running' upload, but they can’t click “Resume” unless the state is 'paused' .

Binding the UI to the upload state simplifies your templates because you don’t have to manage stateful properties on your component like: isPaused, isUploading, and isFinished. The state emits from the observable and you bind the expression in your template.

Simple downloads

Downloading files from Cloud Storage is real simple with AngularFireStorage. The .getDownloadURL() method on an AngularFireStorageReference returns an observable of a download URL.

Combining the .getDownloadURL() method with the *ngIf directive and async pipe allows you to display the image once it’s downloaded.

Just because uploading and downloading files is easy, it doesn’t mean we can’t secure them from unauthorized access.

Secure your files

Cloud Storage for Firebase comes with a server security rule set. These security rules ensures the proper file formats are uploaded and only the right users have access to the right files.

The <input type="file" access=".png,.jpg"> tag restricts file uploads to .png and .jpg. However, this a client only restriction. It has nothing to do with what Cloud Storage will accept. To secure your storage bucket you need to write rules that only allow images to be uploaded at the /images path.

The rules above will let anyone read from the /images path, but only upload a file if it has an 'image/.*' content type (.png, .jpg, .jpeg, etc…) and is less than 5MB.

Get started and even contribute!

Get started by checking out our Github repo and the official documentation. AngularFire and the Cloud Storage SDK are each open source projects. If you want to get involved in the project make sure to check out our open issue or file one yourself!