typescript - Angular 6 Descargar archivo desde API REST
Tengo mi API REST donde pongo mi archivo PDF, ahora quiero que mi aplicación Angular lo descargue al hacer clic a través de mi navegador web, pero obtengo HttpErrorResponse
"Token inesperado % en JSON en la posición 0"
"SyntaxError: token inesperado % en JSON en la posición 0↵ en JSON.parse (
este es mi punto final
@GetMapping("/help/pdf2")
public ResponseEntity<InputStreamResource> getPdf2(){
Resource resource = new ClassPathResource("/pdf-sample.pdf");
long r = 0;
InputStream is=null;
try {
is = resource.getInputStream();
r = resource.contentLength();
} catch (IOException e) {
e.printStackTrace();
}
return ResponseEntity.ok().contentLength(r)
.contentType(MediaType.parseMediaType("application/pdf"))
.body(new InputStreamResource(is));
}
este es mi servicio
getPdf() {
this.authKey = localStorage.getItem('jwt_token');
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/pdf',
'Authorization' : this.authKey,
responseType : 'blob',
Accept : 'application/pdf',
observe : 'response'
})
};
return this.http
.get("http://localhost:9989/api/download/help/pdf2", httpOptions);
}
e invocación
this.downloadService.getPdf()
.subscribe((resultBlob: Blob) => {
var downloadURL = URL.createObjectURL(resultBlob);
window.open(downloadURL);});
Respuesta #1
Lo resolví de la siguiente manera:
// header.component.ts
this.downloadService.getPdf().subscribe((data) => {
this.blob = new Blob([data], {type: 'application/pdf'});
var downloadURL = window.URL.createObjectURL(data);
var link = document.createElement('a');
link.href = downloadURL;
link.download = "help.pdf";
link.click();
});
//download.service.ts
getPdf() {
const httpOptions = {
responseType: 'blob' as 'json')
};
return this.http.get(`${this.BASE_URL}/help/pdf`, httpOptions);
}
Respuesta #2
Resolví el problema de esta manera (por favor, tenga en cuenta que he combinado múltiples soluciones encontradas en Stack Overflow, pero no puedo encontrar las referencias. Siéntete libre de agregarlas en los comentarios).
En mi servicio tengo:
public getPDF(): Observable<Blob> {
//const options = { responseType: 'blob' }; there is no use of this
let uri = '/my/uri';
// this.http refers to HttpClient. Note here that you cannot use the generic get<Blob> as it does not compile: instead you "choose" the appropriate API in this way.
return this.http.get(uri, { responseType: 'blob' });
}
En el componente, tengo (esta es la parte combinada de múltiples respuestas):
public showPDF(fileName: string): void {
this.myService.getPDF()
.subscribe(x => {
// It is necessary to create a new blob object with mime-type explicitly set
// otherwise only Chrome works like it should
var newBlob = new Blob([x], { type: "application/pdf" });
// IE doesn't allow using a blob object directly as link href
// instead it is necessary to use msSaveOrOpenBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(newBlob, fileName);
return;
}
// For other browsers:
// Create a link pointing to the ObjectURL containing the blob.
const data = window.URL.createObjectURL(newBlob);
var link = document.createElement('a');
link.href = data;
link.download = fileName;
// this is necessary as link.click() does not work on the latest firefox
link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
setTimeout(function () {
// For Firefox it is necessary to delay revoking the ObjectURL
window.URL.revokeObjectURL(data);
link.remove();
}, 100);
});
}
El código anterior funciona en IE, Edge, Chrome y Firefox. Sin embargo, no me gusta mucho, ya que mi componente está contaminado con cosas específicas del navegador que seguramente cambiarán con el tiempo.
Respuesta #3
Para Angular 12+, se me ocurrió algo así:
this.ApiService
.getFileFromApi()
.pipe(take(1))
.subscribe((response) => {
const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(new Blob([response.body], { type: response.body.type }));
const contentDisposition = response.headers.get('content-disposition');
const fileName = contentDisposition.split(';')[1].split('filename')[1].split('=')[1].trim();
downloadLink.download = fileName;
downloadLink.click();
});
La suscripción está en un simple get() con el HttpClient de Angular.
// api-service.ts
getFileFromApi(url: string): Observable<HttpResponse<Blob>> {
return this.httpClient.get<Blob>(this.baseApiUrl + url, { observe: 'response', responseType: 'blob' as 'json'});
}
