Angular 2 descarga PDF de API y lo muestra en la vista

Estoy aprendiendo Angular 2 Beta. Me pregunto cómo descargar el archivo PDF de la API y mostrarlo en mi opinión? Intenté hacer una solicitud usando lo siguiente:

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); }) 
  • Tenga en cuenta que solo uso console.log para ver el valor de r

Pero siempre recibo el siguiente mensaje de excepción:

El método “arrayBuffer ()” no se implementó en la superclase de respuesta

¿Es porque ese método aún no está listo en Angular 2 Beta? ¿O hay algún error que cometí?

Cualquier ayuda sería apreciada. Muchas gracias.

De hecho, esta característica aún no está implementada en el soporte HTTP.

Como solución alternativa, debe extender la clase BrowserXhr de Angular2 como se describe a continuación para establecer el tipo de responseType en blob en el objeto xhr subyacente:

 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); } } 

Luego debe envolver la carga de respuesta en un objeto Blob y usar la biblioteca FileSaver para abrir el diálogo de descarga:

 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); }); } 

La biblioteca de FileSaver debe incluirse en su archivo HTML:

  

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

Desafortunadamente esto establecerá el tipo de responseType para todas las solicitudes AJAX. Para poder establecer el valor de esta propiedad, hay más actualizaciones para hacer en las clases XHRConnection y Http .

Como referencias ver estos enlaces:

  • Descargue el archivo pdf usando jquery ajax
  • Reciba el archivo zip, angularJs

Editar

Después de pensar un poco más, creo que podría aprovechar los inyectores jerárquicos y configurar este proveedor solo al nivel del componente que ejecuta la descarga:

 @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); }); } }

Esta anulación solo se aplicaría a este componente (no se olvide de eliminar el suministro correspondiente al momento de iniciar su aplicación). El componente de descarga podría usarse así:

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

Así que aquí es cómo me las arreglé para que funcione. Mi situación: necesitaba descargar un PDF desde mi punto final API y guardar el resultado como un PDF en el navegador.

Para admitir el guardado de archivos en todos los navegadores, utilicé el módulo FileSaver.js .

Creé un componente que toma la ID del archivo para descargar como parámetro. El componente, , se llama así:

  

El componente usa XHR para buscar / guardar el archivo con el número dado en el parámetro no . De esta forma podemos eludir el hecho de que el módulo http de Angular2 aún no admite tipos de resultados binarios.

Y ahora, sin más preámbulos, el código del 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(); } } 

He usado Font Awesome para las fonts usadas en la plantilla. Quería que el componente muestre un botón de descarga y una ruleta mientras se busca el pdf.

Además, note que podría usar require para buscar el módulo fileSaver.js. Esto es porque estoy usando WebPack así que puedo requerir / importar como yo quiero. Su syntax puede ser diferente dependiendo de su herramienta de comstackción.

No creo que todos estos hacks sean necesarios. Acabo de hacer una prueba rápida con el servicio http estándar en angular 2.0, y funcionó como se esperaba.

 /* 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 */ } ); } 

Esta es la forma más sencilla de descargar un archivo de una API que pude encontrar.

 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); }); } } 

Llame al método downloadFile(api,fileName) desde su clase de componente.

Para obtener FileSaver ejecuta los siguientes comandos en tu terminal

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

Hola , aquí hay un ejemplo de trabajo . ¡También es adecuado para PDF! application / octet-stream – tipo general. 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:

Servicio 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 los paquetes de archivo npm de archivos “protector de archivos”: “^ 1.3.3”, “@ types / file-saver”: “0.0.0” e incluya en vendor.ts import ‘file-saver’;

Componente btn descarga.

 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); } ); } } 

Utilizando

  

Aquí está el código que funciona para descargar la API respone en IE y Chrome / Safari. Aquí la variable de respuesta es respuesta 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 que Filesaver funcione en Angular 5: Instalar

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

En su componente use import * as FileSaver from "file-saver";

y usa FileSaver. predeterminado y no FileSaver. Guardar como

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