Spring Boot 2.7.5 WebClient Multipart文件上传报400错误,求解决方案
问题
基于Spring Boot 2.7.5的WebClient应用调用同版本的多部件文件上传API时,返回400 Bad Request错误,但curl和Angular的POST请求均可正常调用该API。
WebClient端原代码
var home = "/home/me"; var uploadFile = new File(home+"/upload.csv"); var uploadPath = uploadFile.toPath(); var uploadContent = Files.readString(uploadPath); var multipartBodyBuilder = new MultipartBodyBuilder(); multipartBodyBuilder.part("file", uploadPath); //multipartBodyBuilder.part("file", uploadFile); var mimeMultipartData = multipartBodyBuilder.build(); var configuration = new Configuration(/* ... */); var webClient = WebClient.builder() .baseUrl(_baseUrl(configuration.getEndpoint())) .filters( filters -> { filters.add(authentication(configuration)); if (configuration.capturePackets()) { filters.add(captureRequest()); filters.add(captureResponse()); } }) .build(); Mono<HttpStatus> httpStatusMono = webClient .post() .uri(URI.create("http://localhost:8080/api/uploads")) .body(BodyInserters.fromMultipartData(mimeMultipartData)) .retrieve() .bodyToMono(HttpStatus.class);
服务端API代码
@PostMapping("/uploads") public ResponseEntity<?> uploads(@RequestParam(value="file", required=true) MultipartFile file) throws IOException { try (InputStream input = file.getInputStream()) { doSomething(input); } return ResponseEntity.ok().build(); }
错误信息
org.springframework.web.reactive.function.client.WebClientResponseException$BadRequest: 400 Bad Request from POST http://localhost:8080/api/uploads at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:217) Suppressed: The stacktrace has been enhanced by Reactor, refer to additional information below: Error has been observed at the following site(s): *__checkpoint ⇢ 400 from POST http://localhost:8080/api/uploads [DefaultWebClient] Original Stack Trace: at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:217) ...(省略部分栈信息)
正常调用示例
curl命令
curl -X POST -v --user "username:password" -H "Content-Type: multipart/form-data" \ --form file=@upload.csv http://localhost:8080/api/uploads
Angular代码
upload(file: File): Observable<any> { const formData: FormData = new FormData() formData.append('file', file, file.name); const observable: Observable<any> = this._http .post(url, formData) .pipe( // map(response => response), share() );
解决方案
问题根源是WebClient构建多部件请求时,直接传入Path对象无法被Spring正确识别为文件资源,导致服务端无法解析出MultipartFile。调整方式如下:
调整后的WebClient代码
var home = "/home/me"; var uploadFile = new File(home + "/upload.csv"); var multipartBodyBuilder = new MultipartBodyBuilder(); // 方式1:直接传入File对象,自动处理文件名与内容类型 multipartBodyBuilder.part("file", uploadFile) .contentType(MediaType.MULTIPART_FORM_DATA); // 方式2:用FileSystemResource包装Path,手动指定文件名(适合需要自定义文件名的场景) // multipartBodyBuilder.part("file", new FileSystemResource(uploadFile.toPath())) // .filename(uploadFile.getName()) // .contentType(MediaType.TEXT_PLAIN); // 根据实际文件类型调整 var mimeMultipartData = multipartBodyBuilder.build(); var configuration = new Configuration(/* ... */); var webClient = WebClient.builder() .baseUrl(_baseUrl(configuration.getEndpoint())) .filters( filters -> { filters.add(authentication(configuration)); if (configuration.capturePackets()) { filters.add(captureRequest()); filters.add(captureResponse()); } }) .build(); // 服务端返回无body响应,用toBodilessEntity()替代bodyToMono(HttpStatus.class)更合理 Mono<ResponseEntity<Void>> responseMono = webClient .post() .uri("http://localhost:8080/api/uploads") .body(BodyInserters.fromMultipartData(mimeMultipartData)) .retrieve() .toBodilessEntity();
关键调整说明
- 正确传递文件资源:必须使用
File或FileSystemResource包装文件路径,Spring才能生成符合多部件规范的请求体,包含必要的文件名和内容类型标识。 - 响应处理优化:服务端返回的是无内容响应,使用
toBodilessEntity()可以避免解析空body时的潜在异常。 - URI简化:直接传入字符串URI即可,无需手动创建
URI对象,WebClient会自动处理。
内容的提问来源于stack exchange,提问作者Jan Nielsen
相关产品推荐
相关产品推荐

