[英]Spring Web Reactive client
我正在尝试使用 Spring Reactive WebClient 将文件上传到 spring 控制器。 控制器非常简单,如下所示:
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<String> uploadFile(
@RequestParam("multipartFile") MultipartFile multipartFile,
@RequestParam Map<String, Object> entityRequest
) {
entityRequest.entrySet().forEach(System.out::println);
System.out.println(multipartFile);
return ResponseEntity.ok("OK");
}
当我将此控制器与 cURL 一起使用时,一切正常
curl -X POST http://localhost:8080/upload -H 'content-type: multipart/form-data;' -F fileName=test.txt -F randomKey=randomValue -F multipartFile=@document.pdf
multipartFile 进入正确的参数,其他参数进入 Map。
当我尝试从 WebClient 执行相同操作时,我被卡住了。 我的代码如下所示:
WebClient client = WebClient.builder().baseUrl("http://localhost:8080").build();
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.set("multipartFile", new ByteArrayResource(Files.readAllBytes(Paths.get("/path/to/my/document.pdf"))));
map.set("fileName", "test.txt");
map.set("randomKey", "randomValue");
String result = client.post()
.uri("/upload")
.contentType(MediaType.MULTIPART_FORM_DATA)
.syncBody(map)
.exchange()
.flatMap(response -> response.bodyToMono(String.class))
.flux()
.blockFirst();
System.out.println("RESULT: " + result);
这会导致 400 错误
{
"timestamp":1510228507230,
"status":400,
"error":"Bad Request",
"message":"Required request part 'multipartFile' is not present",
"path":"/upload"
}
有谁知道如何解决这个问题?
所以我自己找到了解决方案。 事实证明,Spring 确实需要 Content-Disposition 标头来包含上传的文件名,以便将其序列化为控制器中的 MultipartFile。
为此,我必须创建一个支持设置文件名的 ByteArrayResource 子类
public class MultiPartResource extends ByteArrayResource {
private String filename;
public MultiPartResource(byte[] byteArray) {
super(byteArray);
}
public MultiPartResource(byte[] byteArray, String filename) {
super(byteArray);
this.filename = filename;
}
@Nullable
@Override
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
}
然后可以使用此代码在客户端中使用
WebClient client = WebClient.builder().baseUrl("http://localhost:8080").build();
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.set("fileName", "test.txt");
map.set("randomKey", "randomValue");
ByteArrayResource resource = new MultiPartResource(Files.readAllBytes(Paths.get("/path/to/my/document.pdf")), "document.pdf");
String result = client.post()
.uri("/upload")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(map))
.exchange()
.flatMap(response -> response.bodyToMono(String.class))
.flux()
.blockFirst();
System.out.println("RESULT: " + result);
您需要在文件部分包含一个文件名才能上传成功,并结合asyncPart()
以避免缓冲所有文件内容,然后您可以这样编写代码:
WebClient client = WebClient.builder().baseUrl("http://localhost:8080").build();
Mono<String> result = client.post()
.uri("/upload")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body((outputMessage, context) ->
Mono.defer(() -> {
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();
Flux<DataBuffer> data = DataBufferUtils.read(
Paths.get("/tmp/file.csv"), outputMessage.bufferFactory(), 4096);
bodyBuilder.asyncPart("file", data, DataBuffer.class)
.filename("filename.csv");
return BodyInserters.fromMultipartData(bodyBuilder.build())
.insert(outputMessage, context);
}))
.exchange()
.flatMap(response -> response.bodyToMono(String.class));
System.out.println("RESULT: " + result.block());
提供内容配置的更简单方法
MultipartBodyBuilder builder = new MultipartBodyBuilder();
String header = String.format("form-data; name=%s; filename=%s", "paramName", "fileName.pdf");
builder.part("paramName", new ByteArrayResource(<file in byte array>)).header("Content-Disposition", header);
// in the request use
webClient.post().body(BodyInserters.fromMultipartData(builder.build()))
在这种情况下使用ByteArrayResource
效率不高,因为整个文件内容将加载到内存中。
使用带有"file:"
前缀的UrlResource
或ClassPathResource
应该可以解决这两个问题。
UrlResource resource = new UrlResource("file:///path/to/my/document.pdf");
我试过@Ozzie 的方法不能让它与 List 一起工作,有什么提示吗?
@PostMapping(
value = "uploadReport",
consumes = {MediaType.MULTIPART_FORM_DATA_VALUE},
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<Mono<Report>> postUploadReport(@RequestPart(name = "titleReport")List<FilePart> titleReport){}
测试代码:
MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();
List<byte[]> listOfTitleReport = new ArrayList<>();
var file = Files.readAllBytes(new ClassPathResource("test.pdf").getFile().toPath());
listOfTitleReport.add(file);
bodyBuilder.part("titleReport", listOfTitleReport).header("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE);
MultiValueMap<String, HttpEntity<?>> body = bodyBuilder.build();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.