Sunday, December 10, 2017

Download File in Angular Js2 Application

Downloading file from web application is one of the feature provided by most of the web application, when user visit web site and application want to share required document (Ex. Dynamically generated report) to visitor of website. Downloading file using serverside front end technology like Asp.Net MVC is very easy, but downloading file using client side language JavaScript is difficult so in turn with angular like client side framework also.

Below post is about how easily file downloading can be achievable in angular framework based web application.

Angular JS2 – TypeScript code 

Below is code in angular which allows downloading file from server

  constructor(private http:Http)
  {}

  DownloadFile():void
  {
    this.getFile("http://localhost:1800/api/demo/GetTestFile")
    .subscribe(fileData =>
      {
      let b:any = new Blob([fileData], { type: 'application/zip' });
      var url= window.URL.createObjectURL(b);
        window.open(url);
      }
    );
  }

  public getFile(path: string):Observable<any>{
    let options = new RequestOptions({responseType: ResponseContentType.Blob});
    return this.http.get(path, options)
        .map((response: Response) => <Blob>response.blob())  ;
  }


Following explain part of code
  1.  Angular component makes use of HTTP service which allows to call server WebAPI which provide file
  2. DownloadFile is function get called from component html , on button click
  3. getFile method in code takes URL of Restful service to which need to be called for getting file as response
  4. When making call to serverside method using HTTP from getFile function , http-get function make use of Request option which specify responsetype as Blob.
  5. One receiving response from server .map() method, map response to <Blob> type.
  6. After getting return value observable  from getFile function, DownloadFile function create Blob of received value and also make use of property type which is “application/zip” it can be other type also like “application/xls”
  7. Finally URL is get created from received blob and it get passed to window.oepn() method where download file dialog get prompt to user for downloading.
WebAPI

Writing code in Angular Js2(typescript) code for downloading file is not enough, there is also need of writing server side code which provide response as file. Below is server side code , this code is done with the help of WebAPI – its framework provided by Microsoft for building web service.
Below is function defined in WebAPI which provide repose as file

[HttpGet]//http get as it return file 
public HttpResponseMessage GetTestFile()
{
    //below code locate physical file on server
    var localFilePath = HttpContext.Current.Server.MapPath("~/timetable.zip");
    HttpResponseMessage response = null;
    if (!File.Exists(localFilePath))
    {
        //if file not found than return response as resource not present
        response = Request.CreateResponse(HttpStatusCode.Gone);
    }
    else
    {
        //if file present than read file
        var fStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read);
        //compose response and include file as content in it
        response = new HttpResponseMessage
        {
            StatusCode = HttpStatusCode.OK,
            Content = new StreamContent(fStream)
        };
        //set content header of reponse as file attached in reponse
        response.Content.Headers.ContentDisposition =
        new ContentDispositionHeaderValue("attachment")
        {
            FileName = Path.GetFileName(fStream.Name)
        };
        //set the content header content type as application/octet-stream as it     
              //returning file as reponse
        response.Content.Headers.ContentType = new
                      MediaTypeHeaderValue("application/octet-stream");
    }
    return response;


Above code is as follows
  1. WebAPI method GetTestFile() , receives request when DownloadFile() function get called in front end
  2. On receiving request it first checks requested file is exists or not, Note: here file is hardcoded but in real time file may be generated dynamically or may be client pass file name as request parameter
  3. If server code not able to locate file then it send response as file i.e. resource not exist on server 
  4. If server code able to find file then , it read file content and compose repose with file need to be deliver (important thing in code is media type format which is "application/octet-stream” )
Full Source code

AppComponent.Html
<input type="button" (click)="DownloadFile()" value="DownloadFile"/>


AppComponent.ts
import { Component } from '@angular/core';
import { Http, Response, Headers, RequestOptions, ResponseContentType } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(private http:Http)
  {}
 
  DownloadFile():void
  {
    this.getFile("http://localhost:1800/api/demo/GetTestFile")
    .subscribe(fileData =>
      {
      let b:any = new Blob([fileData], { type: 'application/zip' });
      var url= window.URL.createObjectURL(b);
        window.open(url);
      }
    );
  }

  public getFile(path: string):Observable<any>{
    let options = new RequestOptions({responseType: ResponseContentType.Blob});
    return this.http.get(path, options)
        .map((response: Response) => <Blob>response.blob())  ;
  }
}


WebAPI code

    public class DemoController : ApiController
    {
        [HttpGet]//http get as it return file
        public HttpResponseMessage GetTestFile()
        {
            //below code locate physcial file on server
            var localFilePath = HttpContext.Current.Server.MapPath("~/timetable.zip");
            HttpResponseMessage response = null;
            if (!File.Exists(localFilePath))
            {
                //if file not found than return response as resource not present
                response = Request.CreateResponse(HttpStatusCode.Gone);
            }
            else
            {
                //if file present than read file
                var fStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read);
                //compose response and include file as content in it
                response = new HttpResponseMessage
                {
                    StatusCode = HttpStatusCode.OK,
                    Content = new StreamContent(fStream)
                };
                //set content header of reponse as file attached in reponse
                response.Content.Headers.ContentDisposition =
                new ContentDispositionHeaderValue("attachment")
                {
                    FileName = Path.GetFileName(fStream.Name)
                };
                //set the content header content type as application/octet-stream as it returning file as reponse
                response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            }
            return response;
        }
    }