You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular-SpringBoot3文件上传下载时的CORS问题求助

解决SpringBoot3 + Angular 文件上传下载的CORS问题

核心问题分析

你的CORS问题主要来自三个方面:Spring Security的CORS配置冲突、前后端请求方式不匹配、文件请求的参数/格式错误,这些问题叠加导致预请求(OPTIONS)无法正确返回CORS头。

具体修复步骤

1. 修复Spring Security的CORS配置

你在SecurityFilterChain中禁用了CORS(.cors().disable()),这会直接覆盖控制器上的@CrossOrigin注解,导致CORS头无法正常添加。正确做法是启用CORS并配置全局规则:

修改SecurityConfiguration:

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {

    private final JwtAuthenticationFilter jwtAuthFilter;
    private final AuthenticationProvider authenticationProvider;
    private final LogoutHandler logoutHandler;

    // 配置全局CORS规则
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200")); // 允许你的前端源
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true); // 如需携带认证信息(如JWT)则开启

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf()
                .disable()
                .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 启用CORS并绑定全局配置
                .authorizeHttpRequests()
                .requestMatchers("/api/v1/**", "/swagger-ui/**", "/v3/api-docs/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authenticationProvider(authenticationProvider)
                .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
                .logout()
                .logoutUrl("/api/v1/auth/logout")
                .addLogoutHandler(logoutHandler)
                .logoutSuccessHandler((request, response, authentication) -> SecurityContextHolder.clearContext())
        ;

        return http.build();
    }
}

配置完成后可移除控制器上的@CrossOrigin注解,全局配置已覆盖相关规则。

2. 修复前后端下载接口的请求方式不匹配

后端下载接口定义为GET请求,但前端使用POST发起请求,且GET请求不支持@RequestBody参数,这会直接导致请求报错,进而影响CORS头的返回。

后端修改:

将下载接口改为POST,同时修正参数逻辑:

@RequestMapping(path = "/download-excel", method = RequestMethod.POST)
public ResponseEntity<byte[]> downloadExcelFile(@RequestBody List<Long> ids) throws IOException {
    ExcelDownloader helper = new ExcelDownloader();
    List<Navire> toDownload = new ArrayList<>();
    for (Long id : ids) {
        toDownload.add(repository.getReferenceById(id));
    }

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
    headers.setContentDispositionFormData("attachment", "entity.xlsx");

    byte[] bytes = helper.downloadToExcel(toDownload);

    return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}

(注:已删除原代码中重复定义的toDownload变量)

前端修改:

直接传递ID列表作为请求体,无需额外设置不必要的请求头:

downloadExcelFile(toDownload: any[]): Observable<HttpResponse<Blob>> {
    const idList: number[] = toDownload.map(value => value.id);
    return this.http.post<Blob>(`${this.API_URL}download-excel`, idList, {
        observe: 'response',
        responseType: 'blob' as 'json'
    }).pipe(
        catchError((error: any) => {
            console.error('Error occurred during file download:', error);
            throw error;
        })
    );
}

3. 修复前端文件上传的格式问题

直接传递File对象会导致请求的Content-Type不是multipart/form-data,后端无法解析@RequestParam("file"),同时可能触发预请求异常。需用FormData包装文件:

uploadExcelFile(file: File): Observable<any> {
    const formData = new FormData();
    formData.append('file', file); // key需与后端@RequestParam的参数名一致
    return this.http.post(`${this.API_URL}upload-excel`, formData);
}

Angular会自动为FormData设置正确的Content-Type,后端即可正常解析文件。

4. 验证预请求有效性

修复完成后,打开浏览器开发者工具的Network面板,找到文件请求的OPTIONS预请求,检查响应头是否包含以下字段:

  • Access-Control-Allow-Origin: http://localhost:4200
  • Access-Control-Allow-Methods: POST, GET, OPTIONS...
  • Access-Control-Allow-Headers: *

若这些字段存在,说明CORS配置已生效。


内容的提问来源于stack exchange,提问作者azerELweed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 09:47:11