Angular 2 Transferir PDF da API e mostrá-lo à vista
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 der
mas recebo sempre a seguinte mensagem de excepção:
Método "arrayBuffer ()" Não implementado na superclasse de respostaÉ porque esse método ainda não está pronto em 2 Beta Angular? Ou há algum erro que eu tenha cometido? Qualquer ajuda seria apreciada. Muito obrigado.
7 answers
Na verdade, este recurso ainda não está implementado no suporte HTTP.
Como uma solução de trabalho, você precisa estender a classe BrowserXhr
de Angular2 como descrito abaixo para definir o responseType
a blob
no objeto 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 <any>(xhr);
}
}
Depois terá de envolver a carga útil de resposta num objecto Blob
e usar a biblioteca de Ficheiros para abrir a janela de transferência:
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 bibliotecade Ficheiros deve ser incluída no seu HTML ficheiro:
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>
Veja este mergulho: http://plnkr.co/edit/tfpS9k2YOO1bMgXBky5Y?p=preview
Infelizmente, isto irá definir oresponseType
para todos os pedidos do AJAX. Para ser capaz de definir o valor desta propriedade, há mais atualizações para fazer nas classes XHRConnection
e Http
.
Como referências, ver estas ligações:
Editar
Depois de pensar um pouco mais, acho que você poderia alavancar injectores hierárquicos e configurar este fornecedor apenas ao nível do componente que executa o download:
@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 anulação só se aplica a este componente (não se esqueça de remover a informação correspondente ao iniciar a sua aplicação). O componente de download pode ser usado assim:
@Component({
selector: 'somecomponent',
template: `
<download filename="'Granizo.pdf'"></download>
`
, directives: [ DownloadComponent ]
})
Para suportar a gravação de ficheiros em todos os navegadores, usei o protector de Ficheiros .módulo js
Eu criei um componente que leva o ID do arquivo para baixar como parâmetro. O componente, , chama-se assim:
<pdf-downloader no="24234232"></pdf-downloader>
O próprio componente usa o XHR para obter/gravar o ficheiro com o número indicado no Não parâmetro. Desta forma, podemos contornar o fato de que o módulo de http Angular2 ainda não suporta tipos de resultados binários.
E agora, sem mais demoras, 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: `
<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();
}
}
Usei o tipo de letra fantástico para os tipos de letra usados no modelo. Eu queria que o componente exibisse um botão de download e um spinner enquanto o pdf é obtido.
Também, repare que eu poderia usar exigir para obter o protector de ficheiros.módulo js. Isto é porque estou a usar WebPack para que eu possa exigir / importar como eu quiser. A sua sintaxe pode ser diferente, dependendo da sua ferramenta de compilação.
/* 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 */ }
);
}
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);
});
}
}
Chama o método downloadFile(api,fileName)
da tua classe de componentes.
Para obter o ficheiro, execute os seguintes comandos no seu terminal
npm install file-saver --save
npm install @types/file-saver --save
Olá , aqui está um exemploa funcionar . Também é adequado para PDF! application/octet-stream-general type. 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 <any>(xhr);
}
}
Instalar pacotes npm "file-saver": "^1.3.3", "@types/file-saver": "0.0.0" e incluir no fornecedor.ts importar o 'protector de ficheiros';
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: '<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);
}
);
}
}
Usando
<download-btn api="api/realisations/realisationsExcel"></download-btn>
Para pôr o filtro a funcionar no Angular 5: Install
npm install file-saver --save
npm install @types/file-saver --save
Na utilização do seu componenteimport * as FileSaver from "file-saver";
E usar o protector de Ficheiros. predefinição e não o protector de Ficheiros.[9]}SaveAs
.subscribe(data => {
const blob = data.data;
const filename = "filename.txt";
FileSaver.default(blob, filename);
Aqui está o código que funciona para downloadign a API respone em IE e chrome / safari. Aqui a variável de resposta é a resposta API.
Nota: A chamada http do cliente precisa de suportar a resposta 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);
}