"I want to download a .pdf file sent from spring-based restful web service to my angular app. How to download it, am I missing some code on my angular app or spring boot?"
I am sending an HTTP GET request from an angular 6 app to my spring-boot server, which generates a .pdf file and then sends me this .pdf file as a blob, but when I try to create a blob on my angular side and open the pdf, it shows the following error :
. ERROR Error: The request body isn't either a blob or an array buffer
I have visited the following questions on StackOverflow, to find some solution: 1. PDF Blob is not showing content, Angular 2 2. How can I get access to the Angular 2 http response body without converting it to string or json? 3. converting byte stream in HTTPResponse body into a pdf file 4. Send File with SpringBoot to Angular2
In Angular app: component:
Service:getPDF(){ this.apiService.getPDF(this.invoiceId) .subscribe( (data) => { //data.blob() doesnt work properly var file = new Blob([data.blob()], { type: 'application/pdf' }) var fileURL = URL.createObjectURL(file); window.open(fileURL); // if you want to open it in new tab var a = document.createElement('a'); a.href = fileURL; a.target = '_blank'; a.download = 'bill.pdf'; document.body.appendChild(a); a.click(); }, (error) => { console.log('getPDF error: ',error); } ); }
getPDF(invoiceId : number) { this.url = this.main_url + '/invoices/generatepdf/'+invoiceId; const headers = new Headers({ 'Content-Type': 'application/json', "Authorization": authorization, responseType : 'blob'}); return this.http.get(this.url, { headers : headers}) .pipe(map( (response) => { return response; }, (error) => { console.log(error.json()); } )); }
In Spring boot:
Controller:
@RestController @RequestMapping("/invoices") public class InvoiceController { @Autowired InvoiceService invoiceService; @GetMapping(path = "/generatepdf/{invoiceId}") public void generateInvoicePdf(@PathVariable Integer invoiceId, HttpServletRequest request,HttpServletResponse response) { invoiceService.generateInvoicePdf(invoiceId, request, response); }
ServiceImplementation:
@Override public String generateInvoicePdf(Integer invoiceId, HttpServletRequest request, HttpServletResponse response) { //createPDF() will create a .pdf file createPDF(pdfFilename, dto, dtoForSupplier); if (pdfFilename != null) { try { File file = new File(pdfFilename); FileInputStream is = new FileInputStream(file); response.setContentType("application/blob"); // Response header response.setHeader("Pragma", "public"); response.setHeader("responseType", "blob"); response.setHeader("Content-Disposition", "attachment; filename=\"" + pdfFilename + "\""); // Read from the file and write into the response OutputStream os = response.getOutputStream(); System.out.println(os); byte[] buffer = new byte[(int) file.length()]; int len; while ((len = is.read(buffer)) != -1) { os.write(buffer, 0, len); } System.out.println(os); os.flush(); os.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } } return pdfFilename; }
I expect to download a .pdf file in the browser and open it and see its contents, but instead i get the error:
core.js:15724 ERROR Error: The request body isn't either a blob or an array buffer at Response.push../node_modules/@angular/http/fesm5/http.js.Body.blob (http.js:782) at SafeSubscriber._next (invoice-details.component.ts:212) at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:196) at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next (Subscriber.js:134) at Subscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._next (Subscriber.js:77) at Subscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54) at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next (map.js:41) at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54) at XMLHttpRequest.onLoad (http.js:1070) at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:423)>
As suggested by @JBNizet , I have implemented the observable as follows:
Component:
Service:
URL's that I referred: