调用 Spring boot api 从 Angular 6 下载 pdf 给出错误无法解析响应
Calling Spring boot api that downloads a pdf from Angular 6 is giving error can't parse response
我有一个 spring boot API 可以创建一个 pdf 报告并下载它,如果我直接在浏览器中调用 API,pdf 将被直接创建和下载,但是当我从 Angular 6 调用 GET API 时,出现以下错误:
Spring开机(java)代码:
@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.compileReport("./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);
}
调用API的Angular代码:(我尝试了多种方法,这里显示了两种):
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');
});
我在 Angular 调用 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"
我只需要调用 API 就像在浏览器中发生的调用一样,以便立即下载 pdf
响应头如下:
Content-disposition
inline; filename=App_report_en.pdf
Content-Type
application/x-pdf
为什么 Angular 没有像我调用 API 时在浏览器中发生的那样下载文件? (尤其是请求成功后)
为避免 JSON 解析问题,您需要在请求选项中使用 responseType: 'blob'
告诉 http 客户端响应将是一个 blob。
然后,要真正让导航器打开文件,您可以按照 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
}
}
});
这应该适用于 IE 10+ 和其他现代浏览器,除了 iOs(参见 CanIuse)
Spring 启动 + Jaspersoft + Angular
在component.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);
});
}
在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' })
};
}));
}
呼叫休息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 开机
public JasperReport getJasperReport(String reportJrxml) throws Exception {
Resource resource = new ClassPathResource(reportJrxml);
InputStream inputStream = resource.getInputStream();
JasperDesign jasperDesign = JRXmlLoader.load(inputStream);
return JasperCompileManager.compileReport(jasperDesign);
}
我有一个 spring boot API 可以创建一个 pdf 报告并下载它,如果我直接在浏览器中调用 API,pdf 将被直接创建和下载,但是当我从 Angular 6 调用 GET API 时,出现以下错误:
Spring开机(java)代码:
@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.compileReport("./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);
}
调用API的Angular代码:(我尝试了多种方法,这里显示了两种):
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');
});
我在 Angular 调用 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"
我只需要调用 API 就像在浏览器中发生的调用一样,以便立即下载 pdf
响应头如下:
Content-disposition
inline; filename=App_report_en.pdf
Content-Type
application/x-pdf
为什么 Angular 没有像我调用 API 时在浏览器中发生的那样下载文件? (尤其是请求成功后)
为避免 JSON 解析问题,您需要在请求选项中使用 responseType: 'blob'
告诉 http 客户端响应将是一个 blob。
然后,要真正让导航器打开文件,您可以按照 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
}
}
});
这应该适用于 IE 10+ 和其他现代浏览器,除了 iOs(参见 CanIuse)
Spring 启动 + Jaspersoft + Angular
在component.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);
});
}
在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' })
};
}));
}
呼叫休息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 开机
public JasperReport getJasperReport(String reportJrxml) throws Exception {
Resource resource = new ClassPathResource(reportJrxml);
InputStream inputStream = resource.getInputStream();
JasperDesign jasperDesign = JRXmlLoader.load(inputStream);
return JasperCompileManager.compileReport(jasperDesign);
}