I'm trying to send a list of files with Spring Rest but I get this exception.
Could not write content: No serializer found for class java.io.ByteArrayInputStream
It works with one file (ByteArrayResource).
It does not work with a list of files (List<ByteArrayResource>).
Here is the important parts of my code:
List<ByteArrayResource> contentsAsResource = new ArrayList<ByteArrayResource>(); for(MultipartFile fichier : fichiers) { contentsAsResource.add(new ByteArrayResource(fichier.getBytes()) { @Override public String getFilename() { return fichier.getOriginalFilename(); } }); }; map.add("files", contentsAsResource); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<LinkedMultiValueMap<String, Object>>(map, headers); FormHttpMessageConverter formConverter = new FormHttpMessageConverter(); formConverter.setMultipartCharset(Charset.forName("UTF8")); formConverter.addPartConverter(new MappingJackson2HttpMessageConverter()); this.restClientHelper.getRestTemplate().getMessageConverters().add(formConverter); this.restClientHelper.getRestTemplate().postForObject("file/save", requestEntity, Object.class); I've tried the following which did not work :
- send an array instead of a list
- wrap the list in a dto
- send bytes[] but it does not work when the file > 1.5Mb
I've been debugging the deserialization but it's such a pain to understand!
If it can help, With one file, the converter 'ResourceHttpMessageConverter' is used.
Anyone have an idea?
EDIT: The request works if I add each file by file in the map (instead of a list).
for (MultipartFile fichier : fichiers) { map.add("files", new ByteArrayResource(fichier.getBytes()) { @Override public String getFilename() { return fichier.getOriginalFilename(); } }); }; but I get another exception : org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:8080/myApp/ws/file/save". There is a basic authentication enabled on the destination server. If i disabled it, everything is working! Here is the spring-security configuration on the destination server.
<http pattern="/ws/file/**" authentication-manager-ref="basicAuthenticationManager" create-session="stateless"> <intercept-url pattern="/**" access="isAuthenticated()"/> <http-basic /> <csrf disabled="true" /> <headers disabled="true" /> </http> Am I missing something in the spring security configuration ?
EDIT 2 : it seems there is not the token "Authorization" in the headers, adding it manually fix the problem
EDIT 3 : Problem solved! When sending multiples files with spring rest template to a destination server with basic auth enabled, it's needed to (re)add the basic authent token. It's well explained here : Basic authentication for REST API using spring restTemplate. I don't know if it's a bug or not (from Spring). My configuration before this update (I chose interceptor way) was this :
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(login, motDePasse)); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); ... final HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setHttpClient(httpClientBuilder.build()); this.restTemplate = new RestTemplate(requestFactory); I've added this :
this.restTemplate.getInterceptors().add(new BasicAuthorizationInterceptor(username, password)); The class is :
private static class BasicAuthorizationInterceptor implements ClientHttpRequestInterceptor { private final String username; private final String password; public BasicAuthorizationInterceptor(String username, String password) { this.username = username; this.password = (password == null ? "" : password); } @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { byte[] token = Base64.getEncoder().encode((this.username + ":" + this.password).getBytes()); request.getHeaders().add("Authorization", "Basic " + new String(token)); return execution.execute(request, body); } }
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l