Pages

Wednesday, February 15, 2017

Download a PDF in Angular 2

If you need to download a PDF file (stream) from your web service, there are a couple of key things to do.

Make sure you use the responseType property of the RequestOptions object (I'm using TypeScript here). You need to set this property to ResponseContentType.Blob (an enum from @angular/http)

Code snippet


// Depending on what you are sending to the server
// and what the server is sending back
let headers = new Headers({ 
   'Content-Type': 'application/json', 
   'Accept', 'application/pdf'
});


let options = new RequestOptions({ headers: headers });
// Ensure you set the responseType to Blob.
options.responseType = ResponseContentType.Blob;


Then, we will use the new Blob object in HTML5 to handle the response. To do this, create a new Blob using the response.blob() function in your map() function.

this.http
   .post(url, body, options)
   .map(response) => {
      // Removed checking of valid response
      ...
      let fileBlob = response.blob();
      let blob = new Blob([fileBlob], { 
         type: 'application/pdf' // must match the Accept type
      });
      ...
   })

Finally, use the saveAs() function of HTML5 to create the file on the client. Due to browser support, it is best to use the FileSaver JavaScript library (npm install file-saver, npm install @types/file-saver)

Don't forget to import it

...
import * as FileSaver from 'file-saver'; 
...

Then just use it after you have created the Blob.

...
let filename = 'mypdf.pdf';
FileSaver.saveAs(blob, filename);
...

Done.

15 comments:

Unknown said...

Well done!

In my case I used:
let url = window.URL.createObjectURL(new Blob([res], {type: 'application/pdf'}));
this.sanitizer.bypassSecurityTrustUrl(url);

insted of file saver, but your solution worked like a charm for me.

Thx!

Anonymous said...

What is sanitizer here?

Unknown said...

import { DomSanitizer, SafeUrl } from '@angular/platform-browser';

Anonymous said...

Can you tell me how it will work?. i need to get pdf file from asp.net controller to angular2 using http get. i have fil stream , how can i send that filestream as response. how to collect that stream in angular2?

Anonymous said...

and how can i store as FileSaver. i need to view this file.

Unknown said...

If the file is very big and you need to stream. Here is something that may help.
https://angular.io/guide/http#listening-to-progress-events

In my case was something more simple, for ex.:

In your request use these parameters:
{headers: new HttpHeaders({'Content-Type': 'application/json', 'Accept': 'application/pdf'}), responseType: 'blob'}

Then pass your response to Blob and open:
const pdfUrl = (window.URL || window['webkitURL']).createObjectURL(new Blob([response], {type: 'application/pdf'}));
const anchor = document.createElement('a');
anchor.download = `${filename}.pdf`;
anchor.href = pdfUrl;
anchor.click();

I updated my code for the new HttpClient but it was working with the old Http before.

Anonymous said...

But I am not able to view the Pdf file. file is downloading but no content in that file. i will share my code with you,

In My WebApi Controller:
========================

[HttpGet]
public HttpResponseMessage Get(string data)
{
var stream = new FileStream(destPath, FileMode.Open,FileAccess.Read);
MemoryStream responseStream = new MemoryStream();
fileStream.Position = 0;
stream.CopyTo(responseStream);
responseStream.Position = 0;
response1.StatusCode = HttpStatusCode.OK;
response1.Content = new StreamContent(responseStream);
byte[] fileData = responseStream.ToArray();
response1.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
string contentDisposition = string.Concat("inline; filename=", fileInfo.FileName);
response1.Content.Headers.Add("x-filename", fileInfo.FileName);
response1.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response1.Content.Headers.ContentDisposition.FileName = fileInfo.FileName;
response1.Content.Headers.ContentLength = fileStream.Length;
return response1;
}


In My Angular2 Component:
========================
downloadFile(){

let url = "/api/values?data=" + fileName;
return this.http.get(url, {
headers: new Headers({
"Access-Control-Allow-Origin": "*",
"Authorization": "Bearer "
}),
responseType: ResponseContentType.Blob
}).subscribe((res: any) => {
const pdfUrl = (window.URL || window['webkitURL']).createObjectURL(new Blob([res], { type: 'application/pdf' }));
const anchor = document.createElement('a');
anchor.href = pdfUrl;
anchor.setAttribute("download", filename);
anchor.click();
});
}

File With FileName What we are setting in anchor.setAttribute("download", filename) is downloading. but inside that content is not showing. it is showing "failed to open pdf document".

can you help me to solve this issue

Unknown said...

I can't help you with the back end, I'm assuming your service is answering correctly. You can check the payload in the network log in your browser to confirm.

Angular

Try adding the header Accept in your request:
{
headers: new Headers({
"Accept": "application/pdf",
"Access-Control-Allow-Origin": "*",
"Authorization": "Bearer "
}),
responseType: ResponseContentType.Blob
}

Anonymous said...

Ok. but i need all the formats to support for this headers. like pdf,img,mp4...

Unknown said...

I trying this example but the browser does not open the file. "Failed to load PDF document.".

If I placed the URL direct in the browser it works perfect.

Unknown said...

Miguel, in your case it might be an option to use the attribute download in the link tag:


Otherwise, make sure all the configuration is right.

Neelu said...

Thank you for such a clear explanation.

I tried in the similar way..

Angular Code:

let headers = new Headers({
'UserID': this.storage.UserID,
'ADUserName': this.storage.ADUserName,
'Content-Type': 'application/json',
'Accept': 'application/pdf'
});


let options = new RequestOptions({ headers: headers });
options.responseType = ResponseContentType.Blob;

this.http.post(
url, body, options).subscribe(
(response) => {
let fileBlob = response.blob();
let blob = new Blob([fileBlob], {
type: 'application/pdf' // must match the Accept type
})

var filename = 'mypdf.pdf';
console.log(blob);
console.log(response);
saveAs(blob, filename);
});



And my Web API looks like this..

[Route("downloading/{invoiceID}")]
[HttpPost]
public HttpResponseMessage DownloadInvoice(int invoiceID)
{
var result = myservice.getResult();
...

HttpResponseMessage r = new HttpResponseMessage(HttpStatusCode.OK);
r.Content = new StreamContent(new MemoryStream(result.Data));
r.Content.Headers.ContentType = new MediaTypeHeaderValue("application/blob");
r.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
r.Content.Headers.ContentDisposition.FileName = result.FileName;
return r;
}

This seems to be not working for me. This fails with 404 error. If I try my original code (shown below), no 404 error. File downloads, but "Failed to load PDF document."

(API is same, except that I changed it to HttpGet Attribute from HttpPost)

same headers/options as above.. and then used get instead of post

this.http.get(url).subscribe(
(response) => {
var mediaType = 'application/pdf';
var blob = new Blob([response.blob], {type: mediaType});
var filename = 'test.pdf';
console.log(blob);
console.log(response);
saveAs(blob, filename);
});


Any inputs please. Have been stuck up on this for long time.
Appreciate your help!

Unknown said...

Hi Neelu,

The error "Failed to load PDF document." could be because of this:

Try
let options = new RequestOptions({ headers: headers, responseType: ResponseContentType.Blob });

Instead of
let options = new RequestOptions({ headers: headers });
options.responseType = ResponseContentType.Blob;

Anonymous said...

I am using following code:-


Download(filename:any)
{
debugger;
const pdfUrl = (window.URL || window['webkitURL']).createObjectURL(new Blob([this.response], {type: 'application/pdf'}));
const anchor = document.createElement('a');
anchor.download = `${filename}.pdf`;
anchor.href = pdfUrl;
anchor.click();
}

File is downloading.but it showing Failed to load pdf. can you help me to clear it.

BRM013 said...

Could be a number of things, browser versions, what is in the actual response. You haven't given me a lot to go off.

One thing maybe, you are trying to create a new BLOB from 'this.response'. Is that the actual blob or are you missing 'this.response.blob'? You need to understand what the data is coming back.