Skip to main content

Download a file with Angular2+ and FileSaver.js

Why should your Angular code download a file?

Of course your webserver can deliver files and you can just add a simple link to your website but what if the file is delivered by a webservice and the webservice requests authentication (i.e. OAuth) to protect the file from unauthorized access?
In this case you might have the requirement to download the file in your code and then tell the browser to save it to the hard disk.

How to do that? 
First of all you need to add filesaver.js to your project. 

npm install file-saver --save

..and add the typings

npm install @types/file-saver --save-dev

More information about filesaver you can find on the filesaver github pages.

To download the file via http you need to implement a service. The following imports are required:

import { Injectable } from '@angular/core';
import { HttpResponseRequestOptionsResponseContentType } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx'

Http of course is required to load data from remote. Response is what is delivered by the http request and you have to get the data from the response. Before that you have to add request options to the call and tell the response content type. You will read more about that in a few moments.
Here is the code of the service:

@Injectable()
export class DownloadService {

  constructor(private httpHttp) { }

  public getFile(pathstring):Observable<Blob>{
      let options = new RequestOptions({responseType: ResponseContentType.Blob});
      
      return this.http.get(pathoptions)
              .map((responseResponse=> <Blob>response.blob())              
              .catch(this.handleError);
  }


Let's explain what is happening here.
public getFile(pathstring):Observable<Blob>

The function returns data of the type Blob which is a container for unchanged raw data. Yes, this is the data you want the browser to save as a file.
To get a Blob you have to tell which response content type you are expecting.
let options = new RequestOptions({responseType: ResponseContentType.Blob});

The request options have to be handed over to the get request.
this.http.get(pathoptions)

If you do not tell the browser the response content type you will see an error message like'The request body isn't either a blob or an array buffer'
The response has a function that can deliver the blob by calling response.blob(). 
Btw. if the file that you want to download from a webservice is protected by i.e. OAuth the options part of the code might look like this:
let headers = new Headers({'Authorization': this.getBearerToken()});
      let options = new RequestOptions({
          headers: headers
          responseType: ResponseContentType.Blob
        });

You just have to add the Authorization header including the token or whatever you use...


How to save the file?

Let's assume you have a component called DownloadComponent which provides a button to click to trigger the file download. In this component you need to import the filesaver.

import * as FileSaver from 'file-saver';

...and make your service visible.

constructor(private apiDownloadService)

Clicking the button calls the function downloadFile in your component.

(click)="downloadFile()"

This function calls the service which loads the data and delivers a Blob.

  downloadFile(){
    
    this.api.getFile("favicon.ico")
      .subscribe(fileData => FileSaver.saveAs(fileData"favicon.ico"));
  }

The function saveAs expects a Blob in the first parameter and the file name in the second parameter. For more information have a look at the filesaver.js documentation at https://github.com/eligrey/FileSaver.js.
FileSaver.saveAs(fileData"favicon.ico")

That's it. In the end the browser should show you that the file has been saved or depending on your browser settings ask you where to save to.


Comments

Popular posts from this blog

Create a simple Messageboard with Firebase and Angular4

You want that your users can leave messages in your Angular Application. In this Article I explain how you can implement this with Angular 4 and Firebase. First we have to import the Moduls for Firebase in our app.module.ts and our component. app.module.ts import  {  AngularFireModule  }  from   'angularfire2' ; import  {  AngularFireDatabaseModule ,  AngularFireDatabase ,  FirebaseListObservable  }  from   'angularfire2/database' ; @ NgModule ({    declarations:  [      AppComponent   ],    imports:  [      BrowserModule ,      FormsModule ,      HttpModule ,      AngularFireModule . initializeApp ( firebaseConfig ),      AngularFireDatabaseModule , We also have to configure the AngularFireModule. export   const   fireba...

Generate documentation for Angular projects with Compodoc

Sometimes the structure of Angular projects can become confusing. After adding tons of components and modules it might be difficult to keep track of what is what. Compodoc can help you out by generating a much better to read documentation for your project. It can give you a visual and easy to browse overview of all the code in your application and lots of useful features. How to install: Compodoc is available by npm. First install it globally: npm install -g @compodoc/compodoc Then install it in your project: npm install --save-dev @compodoc/compodoc In a next step you have to add a command to your package.json. In the 'scripts' area add the following line: "compodoc" :  "./node_modules/.bin/compodoc -p src/tsconfig.app.json" Hint: in the original documentation at the moment it refers to tsconfig.json which is wrong and would give you an error message. Finally generate the documentation and open it in the browser: npm run compodoc co...

Angular 2: Multiple instances in the same Application

Requirement:  Use multiple Root-Components in the same Application  This Root-Components use the same Implementations and Services Display the Root-Components on the same Single-Page Solution: App-Module preparation: import { BrowserModule, DOCUMENT } from '@angular/platform-browser'; import { NgModule, Inject, Component } from '@angular/core'; export class AppModule { private browser_document; ngDoBootstrap(appRef){ if(this.browser_document.getElementsByTagName('app-root1').length > 0){appRef.bootstrap(AppComponent1);} if(this.browser_document.getElementsByTagName('app-root2').length > 0){appRef.bootstrap(AppComponent2);} } constructor(@Inject(DOCUMENT) private document: any){ this.browser_document = document; } } Service preparation: Angular 2 has two options to provide a service. 1. Single instance For a multiple implementation of more Root-Components we need the second opt...