Closed
Description
Greg Adams opened SPR-13571 and commented
I've been trying to send a multipart post via restTemplate and have been unable to get it to work with anything but FileSystemResource. In my use case (a weird file-forwarding use case) this forces me to copy a MultiPartFile InputStream into a temp file in order be able to create a FileSystemResource, which seems undesirable.
Here's a testing version of the file-receiving controller (from another project, running in another servlet container):
@RestController
public class FileReceiveController {
private Log log = LogFactory.getLog(FileReceiveController.class);
@RequestMapping(method = RequestMethod.POST)
public void uploadFile(@RequestParam("customerId") int customerId, @RequestPart("file") MultipartFile multipartFile) {
log.info("customerId: " + customerId);
log.info("Received multipart file - original filename: " + multipartFile.getOriginalFilename());
log.info("content-type: " + multipartFile.getContentType());
log.info("size: " + multipartFile.getSize());
}
}
Here's the file-forwarding controller:
@RestController
public class FileForwardController {
private RestTemplate restTemplate;
public FileForwardController() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
this.restTemplate = new RestTemplate(requestFactory);
}
@RequestMapping(method = RequestMethod.POST)
public void uploadFile(@RequestParam("customerId") int customerId, @RequestPart("file") MultipartFile multipartFile) {
MultiValueMap<String,Object> parts = new LinkedMultiValueMap<>();
parts.add("customerId", customerId);
try {
// copy to temp file and use FileSystemResource
// File tempFile = File.createTempFile("xyz", "");
// FileCopyUtils.copy(multipartFile.getInputStream(), new FileOutputStream(tempFile));
// parts.add("file", new FileSystemResource(tempFile));
// OR use InputStreamResource (broken)
parts.add("file", new InputStreamResource(multipartFile.getInputStream()));
// OR use ByteArrayResource (broken)
// parts.add("file", new ByteArrayResource(multipartFile.getBytes()));
} catch (IOException e) {
throw new RuntimeException(e);
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String,Object>> request = new HttpEntity<>(parts, headers);
restTemplate.exchange("http://localhost:8080", HttpMethod.POST, request, Void.class);
}
}
In this form, the restTemplate.exchange call throws
org.springframework.web.client.HttpClientErrorException: 400 Bad Request
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:614)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:570)
The ByteArrayResource form does the same thing. Only the FileSystemResource form works.
Affects: 4.1.7
Issue Links:
- Read large data using InputStreamResource at ResourceHttpMessageConverter [SPR-13443] #18023 Read large data using InputStreamResource at ResourceHttpMessageConverter
- RestTemplate with InputStreamResource does not work if Content-Length is not set [SPR-12017] #16633 RestTemplate with InputStreamResource does not work if Content-Length is not set
- HTTP Response should not contain both Transfer-Encoding and Content-Length headers [SPR-15212] #19776 HTTP Response should not contain both Transfer-Encoding and Content-Length headers
- Update advice on contentLength() in subclasses of InputStreamResource #20990 Consistent treatment of InputStreamResource subclasses
- Support use of MultipartFile as input to RestTemplate or WebClient [SPR-16808] #21348 Support use of MultipartFile as input to RestTemplate or WebClient
Referenced from: commits 27c1280
0 votes, 6 watchers