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。
我通过尝试这些选项得出了这个结果
- 禁用所有安全性
security:
basic:
enabled: false
management:
security:
enabled: false
- 删除自动配置的 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;
}
我正在尝试将 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。
我通过尝试这些选项得出了这个结果
- 禁用所有安全性
security:
basic:
enabled: false
management:
security:
enabled: false
- 删除自动配置的 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;
}