Spring Boot - потоковая передача Apache FileUpload - поток неожиданно завершился - PullRequest
0 голосов
/ 03 июля 2018

Я пытаюсь использовать Apache Streaming API с Spring Boot (отключен Spring Multipart resolver). Но когда я пытаюсь преобразовать InputStream в byte [], я получаю исключение - Поток неожиданно завершился. Есть идеи, что не так? Пример кода:

//Server side code - Spring Boot REST Controller
@PostMapping
public ResponseEntity<MyObjectDto> upload(HttpServletRequest request)
    {
    FileItemIterator itemIterator;
    MyObject myObject=null;
    if (!ServletFileUpload.isMultipartContent(request)) {
        throw new RuntimeException("Invalid content passed for upload");
    }
    ServletFileUpload servletFileUpload = new ServletFileUpload();
    itemIterator = servletFileUpload.getItemIterator(request);
    while (itemIterator.hasNext()) {
        FileItemStream fileItem = itemIterator.next();
        InputStream inputStream = fileItem.openStream();
        if (!fileItem.isFormField()) {
            byte[] bytes = IOUtils.toByteArray(inputStream);
           //...Pass bytes for further processing
           myObject =doSomething(bytes)
        }
    } 
    return ResponseEntity.ok(Mapper.map(myObject));
}


//Spring  multipart - disabled
spring.http.multipart.enabled=false

// Client code using Apache Streaming API

Request.Post(uri)
              .body(buildMultipartEntityNew(FileUtils.openInputStream(new File("src/test/resources/my.pdf"))))
              .execute()
              .handleResponse(responseHandler);

// Multipart Entity Builder
private static HttpEntity buildMultipartEntity(InputStream inputStream) throws JsonProcessingException {

    return MultipartEntityBuilder.create()
                                 .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
                                 .addBinaryBody("file", inputStream,
                                                ContentType.APPLICATION_OCTET_STREAM,
                                                "teamfile")
                                .build();
}

Исключение:

org.apache.commons.fileupload.MultipartStream $ MalformedStreamException: Поток неожиданно закончился в org.apache.commons.fileupload.MultipartStream $ ItemInputStream.makeAvailable (MultipartStream.java:1005) в org.apache.commons.fileupload.MultipartStream $ ItemInputStream.read (MultipartStream.java:903) в java.io.InputStream.read (InputStream.java:101) в org.apache.commons.io.IOUtils.copyLarge (IOUtils.java:2146) в org.apache.commons.io.IOUtils.copy (IOUtils.java:2102) в org.apache.commons.io.IOUtils.copyLarge (IOUtils.java:2123) в org.apache.commons.io.IOUtils.copy (IOUtils.java:2078) в org.apache.commons.io.IOUtils.toByteArray (IOUtils.java:721)

1 Ответ

0 голосов
/ 11 июля 2018

Я понял проблему. В случае, если кто-то сталкивается с подобной проблемой, первым делом нужно проверить, какие фильтры в цепочке пытаются обернуть / реконструировать запрос (содержащий входной поток). Если вы знаете точный фильтр, вы можете отключить его

 @Bean
    public FilterRegistrationBean registration(PreAuthenticationFilter filter) {
      FilterRegistrationBean registration = new FilterRegistrationBean(filter);
      registration.setEnabled(false);
      return registration;
}

Или вы можете проверить, отключив их все

    public class DefaultFiltersBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory bf)
        throws BeansException {
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)bf;

        Arrays.stream(beanFactory.getBeanNamesForType(javax.servlet.Filter.class))
              .forEach(
                  name -> {
                      BeanDefinition definition =
                          BeanDefinitionBuilder.genericBeanDefinition(FilterRegistrationBean.class)
                                               .setScope(BeanDefinition.SCOPE_SINGLETON)
                                               .addConstructorArgReference(name)
                                               .addConstructorArgValue(new ServletRegistrationBean[] {})
                                               .addPropertyValue("enabled", false)
                                               .getBeanDefinition();

                      beanFactory.registerBeanDefinition(name + "FilterRegistrationBean", definition);
                  });
    }
}

В моем случае пользовательский фильтр безопасности разворачивал запрос и восстанавливал входной поток, а лишь частично восстанавливал входной поток и, следовательно, ошибку - MalformedStream.

Я пришел к этому, попробовав эти варианты

  1. Отключение всей безопасности

security:
  basic:
   enabled: false
management:
  security:
   enabled: false
  1. Удаление автоматически настроенных пружин по умолчанию

spring:
 autoconfigure:
   exclude: org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration

Однажды я понял, что проблема в безопасности. Я отключил защиту только для POST с inputtream, и inputtream идет невредимым.

 @Override public void configure(WebSecurity web) throws Exception {
    web.ignoring()
       .requestMatchers(ignoreNonJsonMediaTypes())
       .and()
       .ignoring()
       .antMatchers(HttpMethod.POST);

    super.configure(web);
}

private MediaTypeRequestMatcher ignoreNonJsonMediaTypes() {
    MediaTypeRequestMatcher
        matcher =
        new MediaTypeRequestMatcher(new HeaderContentNegotiationStrategy(), MediaType.APPLICATION_JSON);
    matcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
    return matcher;
}
...