Подтвердить что ты не робот

Spring Rest - исключение при отправке списка файлов

Я пытаюсь отправить список файлов с помощью Spring Rest, но я получаю это исключение.

Could not write content: No serializer found for class java.io.ByteArrayInputStream

Он работает с одним файлом (ByteArrayResource).

Он не работает со списком файлов (List<ByteArrayResource>).

Вот важные части моего кода:

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);

Я пробовал следующее, которое не срабатывало:

  • отправить массив вместо списка
  • обернуть список в dto
  • отправить байты [], но он не работает, когда файл > 1.5Mb

Я отлаживал десериализацию, но это такая боль, чтобы понять!

Если это может помочь, с одним файлом используется конвертер ResourceHttpMessageConverter.

У кого-нибудь есть идея?

EDIT: запрос работает, если я добавляю каждый файл по файлу на карте (вместо списка).

for (MultipartFile fichier : fichiers) {
  map.add("files", new ByteArrayResource(fichier.getBytes()) {
    @Override
    public String getFilename()
    {
      return fichier.getOriginalFilename();
    }
  });
};

но я получаю другое исключение: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:8080/myApp/ws/file/save". На целевом сервере включена базовая аутентификация. Если я отключил его, все работает! Вот конфигурация spring -security на целевом сервере.

<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>

Я что-то упустил в конфигурации безопасности Spring?

ИЗМЕНИТЬ 2: похоже, в заголовках нет токена "Авторизация", добавив его вручную, исправить проблему

ИЗМЕНИТЬ 3: Проблема решена! При отправке многократных файлов с шаблоном отдыха Spring на целевой сервер с включенным базовым авторизацией необходимо (re) добавить базовый токен аутентификации. Здесь хорошо объяснено: Базовая аутентификация для REST API с помощью Spring restTemplate. Я не знаю, была ли это ошибка или нет (от Spring). Моя конфигурация перед этим обновлением (я выбрал путь перехватчика):

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);

Я добавил следующее:

this.restTemplate.getInterceptors().add(new BasicAuthorizationInterceptor(username, password));

Класс:

 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);
   }

  }
4b9b3361

Ответ 1

в файле конфигурации безопасности spring отсутствует поставщик проверки подлинности. вы можете попробовать это вместо перехватчика

<authentication-manager>
   <authentication-provider>
       <user-service>
       <user name="user" password="password"  />
       </user-service>
   </authentication-provider>
</authentication-manager>