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

Social Login with Angular 4 and FireBase

There is a way to let users use there social logins to get access to Firebase. But how do you implement this login to your Angular 4 project. In this article I will tell you how to set up your Firebase database and how do you implement social login to your angular project. Set up Firebase First we have to get our database ready. In your Firebase Console you will find Authentication in the left navigation bar. At the tab Sign-In Method you can enable different sign in methods. In our example we will enable Google and Facebook, as it's the most commend social login methods these days. The Google Login will be configured automatically. When you enable Facebook Login you need an App ID and an App Secret. You will get these when you register yourself as a developer at Facebook. You can read how you can do that at the Facebook Site . I will not explain you that in detail because it's very easy. If we done, it's time to set up our Angular4 code. Install Ang...

Utilize 3rd party Javascript libraries in your Angular 2+ application with CLI

Not everything is available as a native Typescript library. Due to that sometimes you might need to use 3rd party libraries. After creating a new project with Angular CLI you will experience that VS Code does not know any libraries you have imported in the header tag and the compiler complains when you try to use this libraries. The reason is: You are missing the typings. The typings describe the functionality of the libary and make that available in your project through intellisense and for the compiler. But before you start creating your own typings do the following checks: 1. Does the vendor of the library provide the typings? If yes, install through npm 2. Are the typings available on definitely typed ? If yes, then take it from there. If this is not the case then you can do your own typings. If you want to do that properly, then you should write the complete set of typings for the library and upload to definitely typed to provide it for the community. OK, this might b...

Setting selector prefix with Angular CLI

If you run this well known Angular CLI command to create a new angular project, your selector prefix is set to selector:   'app-root' , If you want to have another name instead of app, like f.e. the company name, you can go to the Angular CLI JSON and changing the prefix from app to companyname. "prefix" :  "app" , "prefix" :  "companyname" , So if you create a new component with CLI, it will get companyname instead of app at the selector. But this has no effect on the existing components. Of course you can change the name manually. But the best way is, to set the prefix right from the start. The CLI comand for this is simple but effiktive. That's all. Have fun at your Angular projects