admin管理员组

文章数量:1405114

I have a spring boot API that creates a pdf report and download it, if I call the API directly in the browser, the pdf is created and downloaded directly, but when I call that GET API from Angular 6 I get the following error:

The Spring boot (java) code:

@RequestMapping(value = "/app_report/en", method = RequestMethod.GET)
@ResponseBody
public void getEnRpt(HttpServletResponse response, @RequestParam("appNum") String appNum) throws JRException, IOException, SQLException {

    JasperReport jasperReport = JasperCompileManagerpileReport("./src/main/resources/jasperReports/App_created_en.jrxml");

    Connection connection = dataSource.getConnection();

    Map<String, Object> params = new HashMap<>();
    params.put("P_APP_NO", appNum);
    JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, connection);

    response.setContentType("application/x-pdf");
    response.setHeader("Content-disposition", "inline; filename=App_report_en.pdf");

    final OutputStream outStream = response.getOutputStream();
    JasperExportManager.exportReportToPdfStream(jasperPrint, outStream);
}

The Angular code that calls the API:( I tried multiple ways, two are shown here):

this.http.get(this.api_url + reportUrl, {
  responseType: 'blob'
}, ).subscribe((response: File) => {
  console.log('report is downloaded');
});

this.http.get(this.api_url + reportUrl).subscribe(
  (response) => {
    console.log('report is downloaded');
  });

The console error that I get after the Angular calls the API:

error:
SyntaxError: Unexpected token % in JSON at position 0 at JSON.parse (<anonymous>) at XMLHttpRequest.onLoad 
message: "Http failure during parsing for https://localhost:7001/reports/app_report/en?appNum=xxxxxxx"
​
name: "HttpErrorResponse"
​
ok: false
​
status: 200
​
statusText: "OK"
​
url: "https://localhost:7001/reports/app_report/en?appNum=xxxxxx"

I only need to call the API like the call that happens in the browser so that the pdf is downloaded right away

The response headers are as follows:

Content-disposition 
inline; filename=App_report_en.pdf
Content-Type    
application/x-pdf

Why Angular is not downloading the file like what happens in the browser if I call that API? (especially since the request is successful)

I have a spring boot API that creates a pdf report and download it, if I call the API directly in the browser, the pdf is created and downloaded directly, but when I call that GET API from Angular 6 I get the following error:

The Spring boot (java) code:

@RequestMapping(value = "/app_report/en", method = RequestMethod.GET)
@ResponseBody
public void getEnRpt(HttpServletResponse response, @RequestParam("appNum") String appNum) throws JRException, IOException, SQLException {

    JasperReport jasperReport = JasperCompileManager.pileReport("./src/main/resources/jasperReports/App_created_en.jrxml");

    Connection connection = dataSource.getConnection();

    Map<String, Object> params = new HashMap<>();
    params.put("P_APP_NO", appNum);
    JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, connection);

    response.setContentType("application/x-pdf");
    response.setHeader("Content-disposition", "inline; filename=App_report_en.pdf");

    final OutputStream outStream = response.getOutputStream();
    JasperExportManager.exportReportToPdfStream(jasperPrint, outStream);
}

The Angular code that calls the API:( I tried multiple ways, two are shown here):

this.http.get(this.api_url + reportUrl, {
  responseType: 'blob'
}, ).subscribe((response: File) => {
  console.log('report is downloaded');
});

this.http.get(this.api_url + reportUrl).subscribe(
  (response) => {
    console.log('report is downloaded');
  });

The console error that I get after the Angular calls the API:

error:
SyntaxError: Unexpected token % in JSON at position 0 at JSON.parse (<anonymous>) at XMLHttpRequest.onLoad 
message: "Http failure during parsing for https://localhost:7001/reports/app_report/en?appNum=xxxxxxx"
​
name: "HttpErrorResponse"
​
ok: false
​
status: 200
​
statusText: "OK"
​
url: "https://localhost:7001/reports/app_report/en?appNum=xxxxxx"

I only need to call the API like the call that happens in the browser so that the pdf is downloaded right away

The response headers are as follows:

Content-disposition 
inline; filename=App_report_en.pdf
Content-Type    
application/x-pdf

Why Angular is not downloading the file like what happens in the browser if I call that API? (especially since the request is successful)

Share Improve this question edited Aug 1, 2018 at 12:10 Ahmed Elkoussy asked Jul 31, 2018 at 9:26 Ahmed ElkoussyAhmed Elkoussy 8,59812 gold badges67 silver badges92 bronze badges 3
  • It's missing your angular code. Most likely, you need to change responseType to 'blob' in the request's options – David Commented Jul 31, 2018 at 9:49
  • sorry I forgot to put the angular code, basically I am calling the api with the httpClient.get method and subscribe to it logging the response, I tried using the type as blob but I got no errors without saving the file, I will add the code asap but I can't now – Ahmed Elkoussy Commented Jul 31, 2018 at 12:09
  • @David I have added the Angular code, unfortunately even having the responseType as 'blob' didn't help, it only helped avoid the json parsing error but the file was not downloaded, note that calling the api in browser can download automatically the file – Ahmed Elkoussy Commented Aug 1, 2018 at 12:40
Add a ment  | 

2 Answers 2

Reset to default 8

To avoid the JSON parsing issue, you need to tell the http client that the response will be a blob, using responseType: 'blob' in requests options.

Then, to actually have the navigator open the file, you can follow the instructions from this blog

    const fileName = "report.pdf";
    this.http.get(this.api_url + reportUrl, {  responseType: 'blob'})
    .subscribe((blob: Blob) => {
    console.log('report is downloaded');

    if (navigator.msSaveBlob) 
    { 
        // IE 10+
        navigator.msSaveBlob(blob, filename);
    }
    else 
    {
        let link = document.createElement("a");
        if (link.download !== undefined) 
        {
            let url = URL.createObjectURL(blob);
            link.setAttribute("href", url);
            link.setAttribute("download", fileName);
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
        else
        {
            //html5 download not supported
        }
    }   
});

This should work for IE 10+ and other modern browsers, apart from iOs (see CanIuse)

Spring Boot + Jaspersoft + Angular

In ponent.ts

reportGenerate() {
this.reportService.generateReport(this.form.value).subscribe(response => {
  let url = window.URL.createObjectURL(response.data);
  let a = document.createElement('a');
  document.body.appendChild(a);
  a.setAttribute('style', 'display: none');
  a.setAttribute('target', 'blank');
  a.href = url;
  a.download = response.filename;
  a.click();
  window.URL.revokeObjectURL(url);
  a.remove();
}, error => {
  console.log(error);
});

}

In service.ts

   public generateReport(data: Data) {
   
    return this.httpService.getReport(url, data)
      .pipe(map((response) => {
        return {
          filename: 'report.pdf',
          data: new Blob([response], { type: 'application/pdf' })
        };
      }));
  }

call rest api

    getReport(url: any, data: any): Observable<any> {
        const headers = new HttpHeaders({
            'Authorization': 'Bearer ' + this.tokenStoreService.getToken()
        })
        return this.http.post(this.URL + url, data, { headers, responseType: 'arraybuffer' as 'json'});
 }

Spring Boot

public JasperReport getJasperReport(String reportJrxml) throws Exception {
     Resource resource = new ClassPathResource(reportJrxml);
     InputStream inputStream = resource.getInputStream();
     JasperDesign jasperDesign = JRXmlLoader.load(inputStream);

    return JasperCompileManager.pileReport(jasperDesign);
}

本文标签: