How to download files to your device from your API in Ionic Framework V3

With the mobile lifestyle that many people have today, downloading a file to a device is, not only desirable at times, but often necessary. In addition, it’s useful to have the ability to download files from an API.

Stanislav Uloznik
Web Developer

This post discusses how to download files to your device from your API in an Ionic 3 application. By using the File and File Transfer plugins that are available for Ionic you can accomplish this task. First, you install the necessary plugins — File and File Transfer. You can refer to Ionic’s documentation here to install them.

Make certain that you add the plugins to your providers in the main app module. Next, you create a service to handle the download. Using a service takes advantage of Angular’s modularity and reusability. Remember to add the following service to your providers in the main app module.

import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Observable";
import { fromPromise } from "rxjs/observable/fromPromise";
import { FileTransfer, FileTransferObject } from '@ionic-native/file-transfer';
import { Platform, AlertController } from "ionic-angular";
import { File } from "@ionic-native/file";
import { AndroidPermissions } from '@ionic-native/android-permissions';
 
@Injectable()
export class DownloadService {
    constructor(
        private transfer: FileTransfer,
        private file: File,
        private platform: Platform,
        private androidPermissions: AndroidPermissions,
        private alertCtrl: AlertController
    ) {
    }
 
    public downloadFile(fileId: number, fileName: string): Observable<any> {
        return fromPromise(this.performDownload(fileId, fileName, fileExtension));
    }
 
    protected async performDownload(fileId: number, fileName: string){
				// We added this check since this is only intended to work on devices and emulators 
        if (!this.platform.is('cordova')) {
            console.warn('Cannot download in local environment!');
            return;
        }
 
        const fileTransfer: FileTransferObject = this.transfer.create();
 
        let uri = encodeURI(`www.yourapi.com/file/download?id=fileId`);
 
        let path = await this.getDownloadPath();
 
        let fullFileName = fileName + '.' + fileExtension;
 
	// Depending on your needs, you might want to use some form of authentication for your API endpoints
	// In this case, we are using bearer tokens
	let bearerToken = 'yourToken';
 
        return fileTransfer.download(
            uri,
            path + fileName,
            false,
            {
                headers: {
                    "Authorization": `Bearer ${bearerToken}`
                }
            }
        ).then(
            result => {
                this.showAlert(true, fileName);
            },
            error => {
                this.showAlert(false, fileName);
            }
        )
    }
 
    public async getDownloadPath() {
        if (this.platform.is('ios')) {
            return this.file.documentsDirectory;
        }
	
			// To be able to save files on Android, we first need to ask the user for permission. 
			// We do not let the download proceed until they grant access
        await this.androidPermissions.checkPermission(this.androidPermissions.PERMISSION.WRITE_EXTERNAL_STORAGE).then(
            result => {
                if (!result.hasPermission) {
                    return this.androidPermissions.requestPermission(this.androidPermissions.PERMISSION.WRITE_EXTERNAL_STORAGE);
                }
            }
        );
 
        return this.file.externalRootDirectory + "/Download/";
    }
 
    // Here we are using simple alerts to show the user if the download was successful or not
    public showAlert(hasPassed: boolean, fileName: string) {
        let title = hasPassed ? "Download complete!" : "Download failed!";
 
        let subTitle = hasPassed ? `Successfully downloaded ${fileName}.` : `There was a problem while downloading ${fileName}`;
 
        const alert = this.alertCtrl.create({
            title: title,
            subTitle: subTitle,
            buttons: ['OK']
        });
        alert.present();
    }
}

Next, you inject the download service into the page and add a function to be called from your template. That code looks as follows.

import { Component} from '@angular/core';
import { IonicPage, NavController } from 'ionic-angular';
import { DownloadService } from '../../providers/download/download-service';
 
@IonicPage()
@Component({
  selector: 'page-downloads',
  templateUrl: 'downloads.html',
})
export class DownloadsPage {
 
  // Here we are injecting our download service into our page
  constructor(private downloadService: DownloadService{ }
 
  // This is the function that will trigger the download
  public downloadFile(fileId: number, fileName: string) {
    this.downloadService.downloadFile(fileId, fileName).subscribe();
  }
 
}

Finally, add the necessary code to call the function from page template. The assumption is that you have a list of items to be downloaded.

<ion-list>
  <ng-container *ngFor="let item of downloadableItems">
    <button (click)="downloadFile(item.id, item.name)">
      <div>
        <h2>{{ content.name }}</h2>
      </div>
    </button>
  </ng-container>
</ion-list>

In summary, you create a service to handle the download. Then, you inject the service into the page and add a function to be called from the template. Finally, you call the function. Now, you can successfully download files from an API to your device.