Angular 2 download PDF da API e exibi-lo na visualização

Estou aprendendo o Angular 2 Beta. Gostaria de saber como baixar o arquivo PDF da API e exibi-lo na minha opinião? Eu tentei fazer uma solicitação usando o seguinte:

var headers = new Headers(); headers.append('Accept', 'application/pdf'); var options = new ResponseOptions({ headers: headers }); var response = new Response(options); this.http.get(this.setUrl(endpoint), response).map(res => res.arrayBuffer()).subscribe(r=>{ console.log(r); }) 
  • Por favor, note que eu só uso o console.log para ver o valor de r

Mas sempre recebo a seguinte mensagem de exceção:

Método “arrayBuffer ()” não implementado na superclass Response

É porque esse método ainda não está pronto no Angular 2 Beta? Ou há algum erro que cometi?

Qualquer ajuda seria apreciada. Muito obrigado.

Na verdade, esse recurso ainda não está implementado no suporte HTTP.

Como solução alternativa, você precisa estender a class BrowserXhr de Angular2 conforme descrito abaixo para definir o responseType como blob no object xhr subjacente:

 import {Injectable} from 'angular2/core'; import {BrowserXhr} from 'angular2/http'; @Injectable() export class CustomBrowserXhr extends BrowserXhr { constructor() {} build(): any { let xhr = super.build(); xhr.responseType = "blob"; return (xhr); } } 

Em seguida, você precisa agrupar a carga útil da resposta em um object Blob e usar a biblioteca FileSaver para abrir a checkbox de diálogo de download:

 downloadFile() { this.http.get( 'https://mapapi.apispark.net/v1/images/Granizo.pdf').subscribe( (response) => { var mediaType = 'application/pdf'; var blob = new Blob([response._body], {type: mediaType}); var filename = 'test.pdf'; saveAs(blob, filename); }); } 

A biblioteca FileSaver deve ser incluída no seu arquivo HTML:

  

Veja este plunkr: http://plnkr.co/edit/tfpS9k2YOO1bMgXBky5Y?p=preview

Infelizmente, isso definirá o responseType para todas as solicitações AJAX. Para poder definir o valor dessa propriedade, há mais atualizações para fazer nas classs XHRConnection e Http .

Como referências, veja estes links:

  • Faça o download do arquivo pdf usando o jquery ajax
  • Receber arquivo zip, angularJs

Editar

Depois de pensar um pouco mais, acho que você poderia alavancar injetores hierárquicos e configurar esse provedor apenas no nível do componente que executa o download:

 @Component({ selector: 'download', template: '
Download
' , providers: [ provide(CustomBrowserXhr, { useClass: CustomBrowserXhr } ] }) export class DownloadComponent { @Input() filename:string; constructor(private http:Http) { } downloadFile() { this.http.get( 'https://mapapi.apispark.net/v1/images/'+this.filename).subscribe( (response) => { var mediaType = 'application/pdf'; var blob = new Blob([response._body], {type: mediaType}); var filename = 'test.pdf'; saveAs(blob, filename); }); } }

Essa substituição só se aplica a esse componente (não se esqueça de remover o recurso correspondente ao inicializar seu aplicativo). O componente de download pode ser usado assim:

 @Component({ selector: 'somecomponent', template: `  ` , directives: [ DownloadComponent ] }) 

Então aqui está como eu consegui fazê-lo funcionar. Minha situação: eu precisava fazer o download de um PDF do endpoint da minha API e salvar o resultado como PDF no navegador.

Para suportar a economia de arquivos em todos os navegadores, usei o módulo FileSaver.js .

Eu criei um componente que leva o ID do arquivo para download como parâmetro. O componente, , é chamado assim:

  

O próprio componente usa o XHR para buscar / salvar o arquivo com o número dado no parâmetro no. Desta forma, podemos contornar o fato de que o módulo http Angular2 ainda não suporta tipos de resultados binários.

E agora, sem mais delongas, o código do componente:

  import {Component,Input } from 'angular2/core'; import {BrowserXhr} from 'angular2/http'; // Use Filesaver.js to save binary to file // https://github.com/eligrey/FileSaver.js/ let fileSaver = require('filesaver.js'); @Component({ selector: 'pdf-downloader', template: `  ` }) export class PdfDownloader { @Input() no: any; public pending:boolean = false; constructor() {} public download() { // Xhr creates new context so we need to create reference to this let self = this; // Status flag used in the template. this.pending = true; // Create the Xhr request object let xhr = new XMLHttpRequest(); let url = `/api/pdf/iticket/${this.no}?lang=en`; xhr.open('GET', url, true); xhr.responseType = 'blob'; // Xhr callback when we get a result back // We are not using arrow function because we need the 'this' context xhr.onreadystatechange = function() { // We use setTimeout to trigger change detection in Zones setTimeout( () => { self.pending = false; }, 0); // If we get an HTTP status OK (200), save the file using fileSaver if(xhr.readyState === 4 && xhr.status === 200) { var blob = new Blob([this.response], {type: 'application/pdf'}); fileSaver.saveAs(blob, 'Report.pdf'); } }; // Start the Ajax request xhr.send(); } } 

Eu usei o Font Awesome para as fonts usadas no modelo. Eu queria que o componente exibisse um botão de download e um controle giratório enquanto o pdf é buscado.

Além disso, observe que eu poderia usar require para buscar o módulo fileSaver.js. Isso é porque estou usando o WebPack para que eu possa exigir / importar como eu quero. Sua syntax pode ser diferente dependendo da sua ferramenta de compilation.

Eu não acho que todos esses hacks são necessários. Acabei de fazer um teste rápido com o serviço http padrão em angular 2.0 e funcionou como esperado.

 /* generic download mechanism */ public download(url: string, data: Object = null): Observable { //if custom headers are required, add them here let headers = new Headers(); //add search parameters, if any let params = new URLSearchParams(); if (data) { for (let key in data) { params.set(key, data[key]); } } //create an instance of requestOptions let requestOptions = new RequestOptions({ headers: headers, search: params }); //any other requestOptions requestOptions.method = RequestMethod.Get; requestOptions.url = url; requestOptions.responseType = ResponseContentType.Blob; //create a generic request object with the above requestOptions let request = new Request(requestOptions); //get the file return this.http.request(request) .catch(err => { /* handle errors */ }); } /* downloads a csv report file generated on the server based on search criteria specified. Save using fileSaver.js. */ downloadSomethingSpecifc(searchCriteria: SearchCriteria): void { download(this.url, searchCriteria) .subscribe( response => { let file = response.blob(); console.log(file.size + " bytes file downloaded. File type: ", file.type); saveAs(file, 'myCSV_Report.csv'); }, error => { /* handle errors */ } ); } 

Aqui está a maneira mais simples de baixar um arquivo de uma API que consegui criar.

 import { Injectable } from '@angular/core'; import { Http, ResponseContentType } from "@angular/http"; import * as FileSaver from 'file-saver'; @Injectable() export class FileDownloadService { constructor(private http: Http) { } downloadFile(api: string, fileName: string) { this.http.get(api, { responseType: 'blob' }) .subscribe((file: Blob) => { FileSaver.saveAs(file, fileName); }); } } 

Chame o método downloadFile(api,fileName) da sua class de componente.

Para obter o FileSaver, execute os seguintes comandos no seu terminal

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

Olá , aqui está um exemplo de trabalho . Também é adequado para PDF! application / octet-stream – tipo geral. Controlador:

 public FileResult exportExcelTest() { var contentType = "application/octet-stream"; HttpContext.Response.ContentType = contentType; RealisationsReportExcell reportExcell = new RealisationsReportExcell(); byte[] filedata = reportExcell.RunSample1(); FileContentResult result = new FileContentResult(filedata, contentType) { FileDownloadName = "report.xlsx" }; return result; } 

Angular2:

Serviço xhr:

 import { Injectable } from '@angular/core'; import { BrowserXhr } from '@angular/http'; @Injectable() export class CustomBrowserXhr extends BrowserXhr { constructor() { super(); } public build(): any { let xhr = super.build(); xhr.responseType = "blob"; return (xhr); } } 

Instale os pacotes npm com proteção de arquivo “file-saver”: “^ 1.3.3”, “@ types / file-saver”: “0.0.0” e inclua em vendor.ts import ‘file-saver’;

Componente btn download.

 import { Component, OnInit, Input } from "@angular/core"; import { Http, ResponseContentType } from '@angular/http'; import { CustomBrowserXhr } from '../services/customBrowserXhr.service'; import * as FileSaver from 'file-saver'; @Component({ selector: 'download-btn', template: '', providers: [ { provide: CustomBrowserXhr, useClass: CustomBrowserXhr } ] }) export class DownloadComponent { @Input() api: string; constructor(private http: Http) { } public downloadFile() { return this.http.get(this.api, { responseType: ResponseContentType.Blob }) .subscribe( (res: any) => { let blob = res.blob(); let filename = 'report.xlsx'; FileSaver.saveAs(blob, filename); } ); } } 

Usando

  

Aqui está o código que funciona para o download da API respone no IE e chrome / safari. Aqui, a variável de resposta é a resposta da API.

  let blob = new Blob([response], {type: 'application/pdf'}); let fileUrl = window.URL.createObjectURL(blob); if (window.navigator.msSaveOrOpenBlob) { window.navigator.msSaveOrOpenBlob(blob, fileUrl.split(':')[1] + '.pdf'); } else { window.open(fileUrl); } 

Para fazer com que o Filesaver funcione no Angular 5: Install

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

Em seu componente use import * as FileSaver from "file-saver";

e use o FileSaver. padrão e não FileSaver. Salvar como

 .subscribe(data => { const blob = data.data; const filename = "filename.txt"; FileSaver.default(blob, filename);