Mastering Cordova's File Navigation in an Ionic Application

I literally spent 2 hours trying to find a good music player for iPhone. Why?

Every music files I downloaded on the web were just lost somewhere and no applications could access it ... Ridiculous right?

In this tutorial we create a file navigation mobile application that allows us to access any file on any device. From there you can create any type of application you want: music player, video player ...

Accessing The Root Folder

As usual the project creation and plugin installation:

ionic start ionic-file-navigation blank ionic cordova plugin add cordova-plugin-file --save

The home.html is quite simple:

< ion-header > < ion-navbar > < ion-title > Ionic File Navigation </ ion-title > </ ion-navbar > </ ion-header > < ion-content padding > < button ion-button (click) = " goUp() " > Go up </ button > < div *ngFor = " let item of items " > < div *ngIf = " item.isDirectory " (click) = " goDown(item) " > Directory: {{item.name}} </ div > < div *ngIf = " item.isFile " > File: {{item.name}} </ div > </ div > </ ion-content >

One button to go to the parent folder and an ngFor that will create a div for each file or directory (the ngIf does the job here).

Two custom methods will be used: goUp and goDown.

goDown will pass the item that has been tapped on. If it's a directory, the files inside this folder will be displayed, if not, nothing happens.

The home.ts initialization is very important:

import { Component , NgZone } from '@angular/core' ; import { Platform } from 'ionic-angular' ; @ Component ( { selector : 'page-home' , templateUrl : 'home.html' } ) export class HomePage { savedParentNativeURLs = [ ] ; items ; constructor ( public plt : Platform , public ngZone : NgZone ) { plt . ready ( ) . then ( ( ) = > { this . listRootDir ( ) ; } ) } }

Two exotic imports there: NgZone and Platform.

The Platform Service is used to start our work once the device is ready.

The NgZone Service will help us update the UI because Angular won't track the changes that occur in Cordova callbacks. If we don't use it, our HomePage properties will be updated but the template won't display the changes.

The HomePage properties are:

items : Where the files and folders will be stocked savedParentNativeURLs : As we navigate deeper and deeper into our folders, we need to keep in memory our way up, just like Hansel and Gretel's trail of bread crumbs

The first action is to list what is inside the root directory:

listRootDir = ( ) = > { const ROOT_DIRECTORY = "file:///" ; ( < any > window ) . resolveLocalFileSystemURL ( ROOT_DIRECTORY , ( fileSystem ) = > { var reader = fileSystem . createReader ( ) ; reader . readEntries ( ( entries ) = > { this . ngZone . run ( ( ) = > { this . items = entries ; } ) ; } , this . handleError ) ; } , this . handleError ) ; }

Many resources out there showcase the requestFileSystem method. However, this method only gives us a sandbox for the iOS platform and in the end ... we don't have access to anything.

In order to have access to every files, "file:///" can be used as the root directory's path.

We need to pass it to the resolveLocalFileSystemURL method. This method is located on the window object. Since we are using TypeScript, we need to change the type of window to any otherwise it won't compile.

Great! We should now receive a fileSystem object, a reader is then extrapolated from there by using the createReader method.

The readEntries method gives us every files and folders located inside our root folder.

The last touch is setting up our items. Since Angular doesn't care about what we are doing, the run method from ngZone will force an UI update when we update our items property with the new entries.

If anything goes wrong, the following handleError method is used:

handleError = error = > { console . log ( "error reading," , error ) ; } ;

Here is our first result:

Funny enough, I'm actually browsing my MacBook's folders using XCode's iPhone Simulator!

Coming next, the goDown method.

Going Up and Down

goDown = item = > { let childName = this . items [ 0 ] . name ; let childNativeURL = this . items [ 0 ] . nativeURL ; const parentNativeURL = childNativeURL . replace ( childName , "" ) ; this . savedParentNativeURLs . push ( parentNativeURL ) ; var reader = item . createReader ( ) ; reader . readEntries ( children = > { this . ngZone . run ( ( ) = > { this . items = children ; } ) ; } , this . handleError ) ; } ;

This method receives an item (or folder).

In order to allow users to navigate back to the root folder, we need to add the parent's url to the savedParentNativeURLs array.

This is done by removing the current item's name from the item's nativeURL's, here is an example:

name: matthieu nativeURL: file:///Users/matthieu parentNativeURLs: [file: ///Users]

Once this is done, we are ready to go down.

Just like before, the createReader method is used on the directory we want to read.

The children are received and the ngZone object is used to update the items in our ngFor loop.

Our final method: goUp.

goUp = ( ) = > { const parentNativeURL = this . savedParentNativeURLs . pop ( ) ; ( < any > window ) . resolveLocalFileSystemURL ( parentNativeURL , ( fileSystem ) = > { var reader = fileSystem . createReader ( ) ; reader . readEntries ( ( entries ) = > { this . ngZone . run ( ( ) = > { this . items = entries ; } ) } , this . handleError ) ; } , this . handleError ) ; }

By popping the savedParentNativeURLs array, we receive the current parent directory's nativeURL and stock it in a parentNativeURL constant.

This one isn't a fileSystem object, it's a just a string!

This string is passed to the resolveLocalFileSystemURL method and the fileSystem object is acquired.

Once again a reader is created, we read our entries and update the UI by using ngZone.

Here is our final result:

Conclusion

Accessing any files and folders in an Ionic mobile application is very simple. All we need is the Cordova plugin file library.

We can start at the root using the “file:///” path or directly go to the Downloads, Music or Videos folders by using the resolveLocalFileSystemURL method.

This method takes a string (ex: “file:///Users/matthieu/Downloads”) and returns a fileSystem object. Once we have this object, the createReader method creates a reader and the readEntries method gives us the content of the directory.