自定义请求/响应对象中的 MultipartFile

MultipartFile inside Custom Request / Response Objects

我们可以在 Rest 模板调用的自定义请求对象中发送 MultipartFile 吗?同样,我们能否在通过 Rest 模板进行的 Rest 调用返回的自定义响应对象中接收 MutlipartFile?

基本上,我们需要在文件上传的同时发送其他基本属性,并在文件下载的同时接收一些属性。

这可以通过 Spring Rest 模板实现吗?

下面是代码,

public class UploadRequest {    
    private Long checkSum;    
    private MultiPartFile fileContents; 
    //setters and getters  
}  

我们从控制器端点接收到 MultipartFile,我们只是将它转发给 使用下面的另一个端点是 Rest 模板调用,

HttpHeaders headers = new HttpHeaders();  
headers.setContentType("mutipart/form-data");  
  
MultiValueMap<String, Object> map = new MultiValueMap<>();  
map.add("uploadRequest", uploadRequest);  
  
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers);  
  
ResponseEntity<UploadResponse> responseEntity = restTemplate.postForEntity(uploadUrl, requestEntity, UploadResponse.class);  

它导致以下异常,

com.fasterxml.jackson.databind.exc.InvalidDefinitionException No Serializer for class java.io.FileDescriptor

no properties discovered to create BeanSerializer

是的,你可以。 Spring 有一个处理多部分文件的 class - org.springframework.web.multipart.MultipartFile。您可以使用它来发送或接收多部分文件。但是请求体需要是form-data。您不能通过 x-www-form-urlencoded.

发送文件

只需在您的自定义请求中使用 MultipartFile 作为属性,您就可以开始了。

@Component
public class CustomRequest {
    ...
    private Long checkSum;
    private MultipartFile file;
    ...
    // make sure you have the getters and setters right
    // also the filed names are spelled exactly the same as they are in the form
    // for example myFile and my_file are different
    
    public MultiValueMap<String, String> getRequestBody() {
        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<String, String>();
        requestBody.add("checkSum", this.getCheckSum());
        requestBody.add("fileContents", this.getFileContents());
        // add other fileds if you need

        return requestBody;
    }
}

在控制器中,

HttpHeaders headers = new HttpHeaders();  
headers.setContentType("mutipart/form-data");    
  
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(customRequest.getRequestBody(), headers);  
  
ResponseEntity<UploadResponse> responseEntity = restTemplate.postForEntity(uploadUrl, requestEntity, UploadResponse.class);

发生的事情是您在 MultiValueMap 内发送请求正文。您必须在 MultiValueMap 内发送表单数据。并确保您的回复包含 getter 和 setter。 Spring 需要 getter setter 来序列化对象。

文档是 here

我们进行了以下更改以使其最终正常运行,

  1. 我们更改了 RestController 端点属性注释,我们有属性 UploadRequest@RequestBody 注释,我们必须根据content-type-multipart-form-databoundary-charset-utf-8-not-supported 中的建议这帮助我们克服了 Http Error 415 - UnsupportedMediaType。

  2. 在客户端,我们根据Rest Api Call over Rest Template with MultipartFile

    中的建议引入classFileSystemResource
public static class FileSystemResource extends ByteArrayResource {

    private String fileName;

    public FileSystemResource(byte[] byteArray , String filename) {
        super(byteArray);
        this.fileName = filename;
    }

    public String getFilename() { return fileName; }
    public void setFilename(String fileName) { this.fileName= fileName; }

}

然后将 rest 调用更改如下,

HttpHeaders headers = new HttpHeaders();  
headers.setContentType("mutipart/form-data");  
  
MultiValueMap<String, Object> map = new MultiValueMap<>();  
map.add("checkSum", request.getCheckSum());
map.add("fileContents", new FileSystemResource(request.getFile().getBytes(), request.getFile().getOriginalFileName()));
  
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers);  
  
ResponseEntity<UploadResponse> responseEntity = restTemplate.postForEntity(uploadUrl, requestEntity, UploadResponse.class);  

经过这些更改后,它起作用了。