Angular调用Spring Boot导出Excel接口遇跨域问题排查
问题原因与解决方案
为什么会出现这个跨域错误?
后端
response.reset()清空了CORS响应头
Spring的@CrossOrigin注解会自动给响应加上Access-Control-Allow-Origin等跨域必需的头,但你在接口代码里调用了response.reset()——这个方法会把所有已设置的响应头(包括Spring刚加的CORS头)全部清空,导致前端请求时服务器返回的响应里没有跨域允许的标识,浏览器直接触发CORS拦截。Angular请求配置不匹配文件导出场景
你当前用的get<void>是默认配置,没有指定响应类型为二进制流(blob),浏览器没法正确解析Excel文件的响应,同时这种配置也可能让跨域校验的逻辑出现异常。
怎么解决?
后端修复(两种方案选其一)
方案1:移除response.reset()
如果业务上不需要完全重置响应,直接删掉这一行就行,Spring通过@CrossOrigin添加的CORS头会保留,而且不影响Excel导出的响应设置:
@CrossOrigin @GetMapping("/ticket/myTicketDownload") public void myTicketDownload(HttpServletResponse response ) throws IOException { // 删掉response.reset() response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String day = DateUtils.localDateformat(LocalDate.now()); String fileName = URLEncoder.encode("myTicket"+day, "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); TicketQueryDto ticketQueryDto = new TicketQueryDto(); ticketQueryDto.setPkMyTicket("111"); List<TicketVo> allTicketData = iTicketGeneralService.getMyTicketData(ticketQueryDto); EasyExcel.write(response.getOutputStream(), TicketVo.class).sheet(day).doWrite(allTicketData); }
方案2:手动添加CORS头(必须保留response.reset()时用)
如果一定要重置响应,就在response.reset()之后手动补上跨域允许的头:
@CrossOrigin @GetMapping("/ticket/myTicketDownload") public void myTicketDownload(HttpServletResponse response ) throws IOException { response.reset(); // 手动添加允许跨域的头,生产环境建议指定具体域名,不要用* response.setHeader("Access-Control-Allow-Origin", "http://localhost:4200"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String day = DateUtils.localDateformat(LocalDate.now()); String fileName = URLEncoder.encode("myTicket"+day, "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); TicketQueryDto ticketQueryDto = new TicketQueryDto(); ticketQueryDto.setPkMyTicket("111"); List<TicketVo> allTicketData = iTicketGeneralService.getMyTicketData(ticketQueryDto); EasyExcel.write(response.getOutputStream(), TicketVo.class).sheet(day).doWrite(allTicketData); }
前端修复
修改Angular的请求,指定响应类型为blob,同时添加文件下载的逻辑(直接发get请求不会自动触发下载):
// Angular exportMyTicket() { this.http.get(`http://localhost:8080/api/ticket/myTicketDownload`, { responseType: 'blob' }) .subscribe((blob) => { // 创建临时下载链接 const downloadUrl = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = downloadUrl; // 可以从响应头获取文件名,或者自定义 link.download = `myTicket${new Date().toISOString().split('T')[0]}.xlsx`; document.body.appendChild(link); link.click(); // 清理临时资源 window.URL.revokeObjectURL(downloadUrl); document.body.removeChild(link); }); }
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

