I try to get pdf file from spring-cloud-config repository and display it in browser but generated pdf is empty (the same number of pages, but no content - white pages).
The controller method:
@RequestMapping(value = {"/file"}, method = RequestMethod.GET)`
public @ResponseBody
ResponseEntity<InputStreamResource> getPdfFile(HttpServletResponse response) throws IOException
{
ResourceWrapper resourceWrapper = new ResourceWrapper("file.pdf", appProperties);
InputStream inputStream = resourceWrapper.getInputStream();
HttpHeaders responseHeaders = new HttpHeaders();
InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
responseHeaders.setContentLength(inputStream.available());
responseHeaders.setContentType(MediaType.valueOf("application/pdf"));
return new ResponseEntity<>(inputStreamResource, responseHeaders, HttpStatus.OK);
}
Can you provide any explanations? Is there a problem with spring-cloud-config configuration (accept only headers: application/json and text/plain content type?) or maybe the problem is with server buffer and can't load pdf ?
Comment From: ryanjbaxter
I dont think this is something the config server was every intended to do. I also dont see from your snippet of code where the config server is even used.
Comment From: marcelkram
It seems that the org.springframework.cloud.config.server.resource.ResourceController#binary
handler is never called.
When requesting a file via standard URL like http://server_adress:port/app-name/profile/label/filename.pdf
the request is handled by org.springframework.cloud.config.server.resource.ResourceController#retrieve
which damages the file by reading it as an UTF-8 encoded string. I've verified this behavior on the Edgware.SR2 release.
Both the binary and retrieve handlers have the same request mapping path. There's an implementation for serving binary files, but it doesn't work. Possible bug? Maybe binary files should have some other mapping, or add a parameter that could inform the server that the requested file should be returned as binary data?
Comment From: spencergibb
You need to request with Accepts: application/octet-stream
which is the selector on ResourceController.retrieve()
.
Comment From: marcelkram
Thanks for your comment spancergibb.
I've double checked. Sadly it doesn't change anything. I don't think that headers work as a selector here. The request is still processed by the ResourceController.retrieve
method.
Request headers:
Host: 192.168.104.10:8888
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36
Accept: application/octet-stream
Accept-Encoding: gzip, deflate
Accept-Language: pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7
Response headers:
HTTP/1.1 200
X-Application-Context: application:8888
Content-Disposition: inline;filename=f.txt
Content-Type: application/pdf;charset=UTF-8
Content-Length: 2305506
Date: Thu, 01 Mar 2018 18:36:59 GMT
Original Accept header as set by Chrome:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
I'll try to provide you with a test case soon.
Comment From: marcelkram
Here you go: https://github.com/marcelkram/cloud-config-binary-bug-testcase
Maybe it will help you verify this issue.
There are four tests: plain text file - works as expected binary file - fails binary file with application/octet-stream header set - also fails binary file read as UTF-8 String (as the server does) - passes
Comment From: marcelkram
Any chance someone will look into this? :)
It's still labeled as "waiting for feedback" despite the fact it has been provided two months ago.
Comment From: asaikali
Being able to pull back .jks or .p12 binary files from the config server with certificates for mTLS is a good example of why serving binary content from the config server is useful.
Comment From: marcelkram
That's exactly one of my use cases (well, it would be if it worked correctly :) ).
Comment From: gpaffett
I ran into a similar issue. After some research(and some educated guesses), I have found.
When a request is made to the Config Server, it attempts content negotiation. As part of Spring MVC's built-in Content Negotiation feature, it uses a PathExtensionContentNegotiationStrategy. This takes the extension of the path that is part of the request and compares it against MIME types to see if it can find a matching MIME Type. By default, Spring MVC favors this mechanism to determine the content over the "Accept" header.
As an example: Given the request http://example.com:8888/app/default/master/my_stuff.pdf it will get the extension of "pdf" and map it to the MIME type of "application/pdf."
Now to continue with the request from the above example. Regardless of what was passed in the "Accept" header, the MIME type of "application/pdf" is what is used. So in Spring Cloud Config Server's ResourceController the RequestMapping has the produces attribute contains "application/octet-stream", but what Spring sees is "image/png" from the PathExtensionContentNegotiationStrategy. This makes it execute the retrieve() method, not the binary() method.
To prevent this you can add:
spring: mvc: media-types: pdf: application/octet-stream This will map the "pdf" extension to MIME type "application/octet-stream" which will use the binary() method rather than the retrieve() method. Since the above property is a Map, you can add all the MIME types needed.
Caveat: As I can see it does not serve the file as it is static content to be rendered in a browser but allows to be downloaded.
Comment From: spencergibb
Let's add @gpaffett media-types as a note for getting binary types
Comment From: marcelkram
I can confirm that the workaround described by @gpaffett works. Setting headers in the request, as suggested before, is not necessary.
I've updated my test showcase: https://github.com/marcelkram/cloud-config-binary-bug-testcase
Nevertheless having to add every file extension into SCC server configuration is rather annoying. It would be nice if it worked properly out of the box.
Comment From: ask4gilles
new property name for SB 2:
spring.mvc.contentnegotiation.media-types.pdf=application/octet-stream
Comment From: akolata
New property name does not work in my case.
Spring Cloud version: Greenwich.RELEASE In application.yaml I have a property:
spring:
mvc:
contentnegotiation:
media-types:
jks: application/octet-stream
and I have test.jks file in my repository. When I try to download it via the config server, default content type is text/plain ```curl -X GET http://localhost:8888///master/test.jks -vvv
- Connected to localhost (::1) port 8888 (#0)
GET ///master/test.jks HTTP/1.1 Host: localhost:8888 User-Agent: curl/7.58.0 Accept: /
< HTTP/1.1 200 < Content-Disposition: inline;filename=f.txt < Content-Type: text/plain;charset=UTF-8 < Content-Length: 4353 < Date: Wed, 27 Feb 2019 15:54:16 GMT
**Comment From: spencergibb**
That's not a spring cloud property, but from boot
**Comment From: akolata**
Right, it's a property from org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties class, but it's visible on my classpath.
My dependencies :
Spring Boot 2.1.2
Spring Cloud Greenwich.RELEASE
dependencies { implementation "org.springframework.boot:spring-boot-starter-web" implementation 'org.springframework.cloud:spring-cloud-config-server' implementation 'org.springframework.cloud:spring-cloud-config-monitor' implementation 'org.springframework.cloud:spring-cloud-starter-stream-rabbit' }
**Comment From: stephanedaviet**
>```
> spring:
> mvc:
> contentnegotiation:
> media-types:
> jks: application/octet-stream
> ```
@akolata Do you mean :
spring: mvc: media-types: jks: application/octet-stream ```
It works for me with those properties, but not yours.
Comment From: asoltesz
Some notes:
1) For Spring Boot 2.x the media types need to be defined under "spring.mvc.contentnegotiation.media-types" and NOT under "spring.mvc.media-types" (maybe that works for SB 1.x).
2) In Spring Boot 2.x: In order to be able to download binary files with the media type registry workaround, one also needs to ensure that the extension resolver is favored otherwise, it will never select the "binary" controller method and the file will be mangled.
This can be achieved by setting this in Spring Boot 2.x:
"spring.mvc.contentnegotiation.favor-path-extension" to "true"
In more detail: https://github.com/spring-projects/spring-boot/issues/13424#issuecomment-403967637
Comment From: nachiappannk
I am able to serve binary files from spring config server
@spencergibb has mentioned to use Accepts: application/octet-stream
in here
There is probably a typo in this. If I use Accept: application/octet-stream
is works perfectly.
I am NOT configuring any property in the server side such as spring.mvc.media-types.docx=application/octet-stream
Comment From: ChiragMoradiya
We countered the same issue and confirm that using request header Accept: application/octet-stream
works.
Great! Thank you!