bademus opened SPR-16445 and commented

It seems the bug was introduced in #16633 org.springframework.http.converter.ResourceHttpMessageConverter#getContentLength

// Don't try to determine contentLength on InputStreamResource - cannot be read afterwards...
// Note: custom InputStreamResource subclasses could provide a pre-calculated content length!
if (InputStreamResource.class == resource.getClass()) {
     return null;
}

In fact default behavior of InputStreamResource is not the same as for its children.


Affects: 4.3.14

Issue Links: - #16633 RestTemplate with InputStreamResource does not work if Content-Length is not set - #18147 resttemplate multipart post with InputStreamResource not working

Comment From: spring-projects-issues

Juergen Hoeller commented

What specifically are you asking for here? I'm aware that such special handling is unusual but unfortunately there is no sensible way to determine the content length for a plain InputStreamResource. And for custom subclasses thereof, we expect contentLength() to be overridden.

Are you trying to implement such a custom InputStreamResource subclass and expose it to ResourceHttpMessageConverter? In most scenarios, it is preferable to implement a regular Resource with a fresh stream returned from getInputStream(), avoiding the InputStreamResource flaw of dealing with a pre-opened stream.

Comment From: spring-projects-issues

bademus commented

Thanks for quick answer,

Actually I have to subclass InputStreamResource to extend metadata.

I was confused a lot when my application change its behavior after I switch InputStreamResource to MyInputStreamResource. I got an exceptional cituation because InputStreamResource was read twice on contentLength and on getting inputStream. I'm thinking about elimination this kind of InputStreamResource behavior if it is possible (I'm aware of legacy background).

Comment From: spring-projects-issues

Juergen Hoeller commented

Before #16633, InputStreamResource itself didn't work with ResourceHttpMessageConverter at all. From that perspective, we at least made that case work. Making it work for InputStreamResource subclasses as well is tough since we don't know whether contentLength() has been overridden there.

So for a custom InputStreamResource subclass, we strongly recommend a custom implementation of contentLength(): possibly simply returning -1, leading to the same behavior as for a standard InputStreamResource in ResourceHttpMessageConverter. I suppose we should at least document that part.

There is indeed a legacy problem here: specifically, that InputStreamResource may be used for content length determination, with contentLength() getting called but not getInputStream() itself. If it wasn't for that specific scenario, we could also let InputStreamResource itself implement contentLength() with a -1 return value.

Comment From: ger0nimo

I encountered the same problem but this code below works for me.

import org.springframework.core.io.ByteArrayResource;
import org.springframework.web.multipart.MultipartFile;

public class MultipartFileResource extends ByteArrayResource {

    private String filename;
    private long contentLength;

    public MultipartFileResource(MultipartFile multipartFile) throws IOException {
        super(multipartFile.getBytes());
        this.filename = multipartFile.getOriginalFilename();
        this.contentLength = multipartFile.getSize();
    }

    @Override
    public String getFilename() {
        return this.filename;
    }
    @Override
    public long contentLength() {
        return this.contentLength;
    }

}