Spring 引导 - 流式传输 Apache FileUpload - 流意外结束

Spring Boot - Streaming Apache FileUpload - Stream ended unexpectedly

我正在尝试将 Apache Streaming API 与 Spring 引导一起使用(已禁用 Spring 多部分解析器)。但是当我尝试将 InputStream 转换为 byte[] 时出现异常 - Stream 意外结束。 知道哪里出了问题吗? 代码示例:

//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: Stream ended unexpectedly at org.apache.commons.fileupload.MultipartStream$ItemInputStream.makeAvailable(MultipartStream.java:1005) at org.apache.commons.fileupload.MultipartStream$ItemInputStream.read(MultipartStream.java:903) at java.io.InputStream.read(InputStream.java:101) at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:2146) at org.apache.commons.io.IOUtils.copy(IOUtils.java:2102) at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:2123) at org.apache.commons.io.IOUtils.copy(IOUtils.java:2078) at org.apache.commons.io.IOUtils.toByteArray(IOUtils.java:721)

我想通了。如果有人遇到类似问题,首先要检查的是链中的任何过滤器都在尝试 wrap/reconstruct 请求(包含输入流)。如果您知道确切的过滤器,您可以禁用它

 @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. 删除自动配置的 Springs 默认安全性

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

一旦我知道安全是个问题。我仅针对具有输入流的 POST 禁用了安全性,并且输入流毫发无损。

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