spring - 如何将临时 PDF/CSV 从后端(Spring)传输到前端(Angular)?
问题描述
我试图达到的目标:我有PDF/CSV
在我的后端生成的服务方法,我想通过按下我前端的按钮来保存该 pdf。
我的第一次尝试是创建文件并PDF/CSV
通过控制器发送整个文件。
@PostMapping(value = "/export")
public File exportReport(
@RequestParam(value = "format", defaultValue = "PDF") ExportFileFormat format,
@RequestBody ExportBody exportBody) {
if (format.equals(ExportFormat.CSV)) {
return reportService.csvExportSummaryCustomerReport(exportBody);
}
if (format.equals(ExportFormat.PDF)) {
return reportService.pdfExportSummaryCustomerReport(exportBody);
}
throw new InvalidWorkingTimeSyntaxException(String.format("Format:%s is invalid.", format));
}
但是这个解决方案给了我一个错误
从源“ http://localhost:4200 ”访问“file:///C:/Users/UserFolder/AppData/Local/Temp/csv6677594787854925068.csv”处的 XMLHttpRequest已被 CORS 策略阻止:跨源请求仅支持协议方案:http、data、chrome、chrome-extension、https。
Ofc 我尝试使用 设置新的设置响应标头 'Access-Control-Allow-Origin' : '*'
,但没有帮助。与 相同 chrome.exe --allow-file-access-from-files --disable-web-security
。
这就是为什么我决定采用另一种方法,即传输bytes[]
和角度创建PDF/CSV
文件。
@PostMapping(value = "/export")
public ResponseEntity<byte[]> exportReport(
@RequestParam(value = "format", defaultValue = "pdf") ExportFileFormat format,
@RequestBody ExportBody exportBody) {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Access-Control-Allow-Origin", "*");
if (format.equals(ExportFileFormat.CSV)) {
responseHeaders.setContentType(MediaType.valueOf("text/csv"));
return new ResponseEntity<>(reportService.csvExportSummaryCustomerReport(exportBody),
responseHeaders,
HttpStatus.OK);
}
if (format.equals(ExportFileFormat.PDF)) {
responseHeaders.setContentType(MediaType.APPLICATION_PDF);
return new ResponseEntity<>(reportService.pdfExportSummaryCustomerReport(exportBody),
responseHeaders,
HttpStatus.OK);
}
throw new InvalidExportFileFormatException(String.format("Format:%s is invalid.", format));
}
现在我添加了标题,后端似乎没问题。之后,我在前端创建了服务:
exportReport(exportBody: ExportBody, format: String): Observable<Object> {
const exportUrl = `${this.reportsUrl}/export?format=${format}`;
if (format == "PDF") {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/pdf',
'Accept': 'application/pdf'
})
};
return this.http.post(exportUrl, exportBody, httpOptions);
}
if (format == "CSV") {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'text/csv',
'Accept': 'text/csv'
})
};
return this.http.post(exportUrl, exportBody, httpOptions);
}
}
现在我想用它来打印结果。
downloadPdf() {
this.hoursWorkedForCustomersService.exportReport(this.exportBody, "PDF").subscribe(
result => {
console.log(result);
//saveAs(result, 'new.csv'); <- in the future.
}
);
}
显然,将来我想下载文件PDF/CSV
,例如
saveAs(result, 'new.pdf');
我有一个错误 406。响应是:
POST http://localhost:4200/export?format=PDF 406.
TypeError: Cannot read property 'message' of null
at SafeSubscriber.next.handle.do.err [as _error] (error.interceptor.ts:25)
at SafeSubscriber.__tryOrSetError (Subscriber.js:240)
at SafeSubscriber.error (Subscriber.js:195)
at Subscriber._error (Subscriber.js:125)
at Subscriber.error (Subscriber.js:99)
at DoSubscriber._error (tap.js:84)
at DoSubscriber.error (Subscriber.js:99)
at XMLHttpRequest.onLoad (http.js:1825)
at ZoneDelegate.webpackJsonp../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421)
at Object.onInvokeTask (core.js:4006)
任何想法我做错了什么?
解决方案
尝试将您的后端方法分成两部分:
@PostMapping(value = "/export", params={"format=PDF"}, produces=MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<byte[]> generatePdf(){..}
@PostMapping(value = "/export", params={"format=CSV"}, produces="text/csv")
public ResponseEntity<byte[]> generateCsv(){..}
请修复请求的 Content-Type:如果您以 UTF-8 格式发送 JSON,这应该可以:
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json;charset=UTF-8',
'Accept': 'application/pdf'
})
};
顺便说一句:不要像这样在控制器中处理 CORS 标头:
responseHeaders.set("Access-Control-Allow-Origin", "*");
考虑@CrossOrigin(origins = "http://localhost:{your frontend server port}")
在控制器上的类或方法级别或全局使用,如下所示:
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:{your frontend server port}");
}
};
}
干杯
推荐阅读
- terraform - 进行 terraform destroy 时无法解码当前后端配置错误
- c# - C# 异步 REST 查询不起作用:WaitAll 或 WhenAll 结合 .Result 挂起
- python - 在 textinfo 注释内进行情节更改
- php - 有没有办法在不引用 PHP 的情况下复制数组?
- elasticsearch - Elasticsearch:我们开始使用 elasticsearch 高级客户端后 CPU 使用率高的模式
- laravel - 在 laravel 中安装身份验证
- c# - 根据半径计算球心,两点
- c++ - 从 submachine 在outerSM 的正交区域上提升 MSM 调用 .process_event
- .net - 如何在不实际编写任何内容的情况下测试目录在跨平台 .NET 中是否可写?
- c++ - quickfix 中的 UtcTimeStamp/UtcDate/LocalTimeStamp/LocalData 类