change - Angular 2 descarga PDF desde la API y lo muestra en vista
router events subscribe angular 4 (7)
Estoy aprendiendo Angular 2 Beta. Me pregunto cómo descargar el archivo PDF desde la API y mostrarlo en mi vista. He intentado hacer una solicitud utilizando 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 der
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 haya cometido?
Cualquier ayuda sería apreciada. Muchas gracias.
Aquí está el código que funciona para descargar la respuesta de la API en IE y chrome / safari. Aquí la variable de respuesta es la respuesta API.
Nota: la llamada http del cliente debe admitir la respuesta de blob.
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);
}
Así que aquí es cómo me las arreglé para hacerlo funcionar. Mi situación: necesitaba descargar un PDF desde mi punto final de API y guardar el resultado como un PDF en el navegador.
Para admitir el guardado de archivos en todos los navegadores, usé el módulo FileSaver.js .
Creé un componente que toma la ID del archivo para descargar como parámetro. El componente, , se llama así:
<pdf-downloader no="24234232"></pdf-downloader>
El componente en sí utiliza XHR para recuperar / guardar el archivo con el número indicado en el parámetro no . De esta manera podemos evitar el hecho de que el módulo http de Angular2 aún no admite tipos de resultados binarios.
Y ahora, sin más dilación, el código 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: `
<button
class="btn btn-secondary-outline btn-sm "
(click)="download()">
<span class="fa fa-download" *ngIf="!pending"></span>
<span class="fa fa-refresh fa-spin" *ngIf="pending"></span>
</button>
`
})
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 fuentes usadas en la plantilla. Quería que el componente mostrara un botón de descarga y un girador mientras se busca el pdf.
Además, tenga en cuenta que podría usar require para recuperar el módulo fileSaver.js. Esto se debe a que estoy usando WebPack, por lo que puedo requerir / importar como quiera. Su sintaxis puede ser diferente dependiendo de su herramienta de construcción.
De hecho, esta característica aún no está implementada en el soporte HTTP.
Como solución alternativa, debe ampliar la clase de Angular2 de BrowserXhr como se describe a continuación para establecer 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 <any>(xhr);
}
}
Luego, debe ajustar la carga útil de respuesta en un objeto Blob
y usar la biblioteca FileSaver para abrir el cuadro de 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:
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>
Ver 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 que hacer en las clases XHRConnection
y Http
.
Como referencias ver estos enlaces:
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: ''<div (click)="downloadFile() ">Download</div>''
, 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 olvide eliminar la provisión correspondiente al iniciar su aplicación). El componente de descarga podría ser usado así:
@Component({
selector: ''somecomponent'',
template: `
<download filename="''Granizo.pdf''"></download>
`
, directives: [ DownloadComponent ]
})
Esta es la forma más sencilla de descargar un archivo desde 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 ejecute los siguientes comandos en su terminal
npm install file-saver --save
npm install @types/file-saver --save
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<Response> {
//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 */ }
);
}
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 use FileSaver. por defecto y no FileSaver. Guardar como
.subscribe(data => {
const blob = data.data;
const filename = "filename.txt";
FileSaver.default(blob, filename);
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 <any>(xhr);
}
}
Instale los paquetes de npm para el protector de archivos "file-saver": "^ 1.3.3", "@ types / file-saver": "0.0.0" e incluya en vendor.ts import ''file-saver'';
Descarga de componentes btn.
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: ''<button type="button" (click)="downloadFile()">Download</button>'',
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
<download-btn api="api/realisations/realisationsExcel"></download-btn>