Use case is:
send files via Streaming using _InputStream_, since _spring webclient_ is not working with _InputStreamResource_, made some modifications created _MultiPartInputStreamFileResource_ (code pasted below)
Using _ByteArrayInputStreamResource_ is not an option for us, it will load everything into memory (causing memory issues) instead of streaming.
Works till 2.1.4.RELEASE of spring-boot-starter-parent using (_MultiPartInputStreamFileResource_ ),
1) 2.1.5.RELEASE of spring-boot-starter-parent -> throws unsupported operation exception
2) 2.1.6.RELEASE of spring-boot-starter-parent ->
unsupported operation exception is resolved, but Getting "Unexpected end of multipart data" from server, while sending files.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
```java
public class MultipartInputStreamFileResource extends InputStreamResource {
private final String filename;
public MultipartInputStreamFileResource(InputStream inputStream, String filename) {
super(inputStream);
this.filename = filename;
}
@Override
public String getFilename() {
return this.filename;
}
@Override
public long contentLength() {
return -1; // we do not want to generally read the whole stream into memory ...
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
MultipartInputStreamFileResource that = (MultipartInputStreamFileResource) o;
return Objects.equals(filename, that.filename);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), filename);
}
}
public class ResourceHttpMessageConverterHandlingInputStreams extends ResourceHttpMessageConverter {
@Override
protected Long getContentLength(Resource resource, MediaType contentType) throws IOException {
Long contentLength = super.getContentLength(resource, contentType);
return contentLength == null || contentLength < 0 ? null : contentLength;
}
}
private BodyInserter, ? super ClientHttpRequest> getMultipartBodyInserter(
InputStream inputStream, String fileName, String version, String messageDigest) {
MultipartInputStreamFileResource multipartInputStreamFileResource =
new MultipartInputStreamFileResource(inputStream, fileName);
MultiValueMap
addHashToMap(multiValueMap, messageDigest);
multiValueMap.put("version", Arrays.asList(version));
multiValueMap.put("file", Arrays.asList(multipartInputStreamFileResource));
return BodyInserters.fromMultipartData(multiValueMap);
}
and use the body inserter in webclient as below,
```java
WebClient webClient = webClientBuilder.baseUrl(url).defaultUriVariables(uriVariables)
.build();
WebClient.RequestBodySpec requestBodySpec = webClient.method(httpMethod);
Mono<ResponseEntity> responseEntityMono = requestBodySpec.body(bodyInserter).exchange()
.flatMap(clientResponse -> clientResponse.toEntity(responseClassType));
First I don't think you need all that code. It should be possible to replace with just a few lines:
MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("file", resource).filename("MyFile.txt");
// ...
webClient.post()
.syncBody(builder.build())
.exchange()
...
2.1.6.RELEASE of spring-boot-starter-parent ->
unsupported operation exception is resolved, but Getting "Unexpected end of multipart data" from server, while sending files.
Secondly, if you're sending files, should that not be a FileSystemResource?
In any case I think I will need a sample to reproduce the issue.
@rstoyanchev
I have attached demo.zip (contains sample code which reproduces the issue),
use "http://localhost:8080/hello" to trigger the api ,
in the pom.xml, please change version of spring boot starter parent accordingly (2.1.4, 2.1.6), works for 2.1.4, not for 2.1.5, 2.1.6
Also from the stack trace from the demo application, thought it's tomcat issue (2.1.4 uses 9.0.17 tomcat core), (2.1.6 uses 9.0.21 tomcat) ,
So the issue is not related to tomcat.
org.apache.tomcat.util.http.fileupload.MultipartStream$MalformedStreamException: Stream ended unexpectedly
at org.apache.tomcat.util.http.fileupload.MultipartStream$ItemInputStream.makeAvailable(MultipartStream.java:983) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.tomcat.util.http.fileupload.MultipartStream$ItemInputStream.read(MultipartStream.java:881) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at java.io.FilterInputStream.read(FilterInputStream.java:133) ~[na:1.8.0_211]
at org.apache.tomcat.util.http.fileupload.util.LimitedInputStream.read(LimitedInputStream.java:132) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at java.io.FilterInputStream.read(FilterInputStream.java:107) ~[na:1.8.0_211]
at org.apache.tomcat.util.http.fileupload.util.Streams.copy(Streams.java:98) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:294) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.connector.Request.parseParts(Request.java:2881) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.connector.Request.parseParameters(Request.java:3214) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.connector.Request.getParameter(Request.java:1116) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:84) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) ~[spring-web-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:853) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587) [tomcat-embed-core-9.0.21.jar:9.0.21]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.21.jar:9.0.21]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_211]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_211]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.21.jar:9.0.21]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_211]
looks like it's not working from reactor netty 0.8.7.RELEASE (tried till 0.8.10.RELEASE , not working), works till 0.8.6.RELEASE
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty</artifactId>
<version>0.8.6.RELEASE</version>
<!-- <scope>compile</scope>-->
</dependency>
Most helpful comment
First I don't think you need all that code. It should be possible to replace with just a few lines:
Secondly, if you're sending files, should that not be a
FileSystemResource?In any case I think I will need a sample to reproduce the issue.