Spring Controller return static HTML site from any directory - spring

I would like to return in my #Controller static HTML website that was generated by other process. Let's say that generated .html files are in /tmp/generated. I'm trying to read file and pass its content to ResponseEntity:
#GetMapping(value = "test")
ResponseEntity<String> test(#RequestParam("filename") String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get("/tmp/generated/" + filename)), "UTF-8");
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_HTML);
return new ResponseEntity<String>(content, headers, HttpStatus.OK);
}
But when I open url in browser I get badly encoded html content (stating and ending with '"'):
"\u003chtml\u003e\n\u003chead\u003e\n \u003cmeta charset\u003d\"utf-8\" /\u003e\n \u003cmeta http-equiv\u003d\"X-UA-Compatible\" content\u003d\"IE\u003dedge\" /\u003e\n \u003cmeta name\u003d\"viewport\" content\u003d\"width\u003ddevice-width, initial-scale\u003d1\" /\u003e [.....]
If I add produces = MediaType.TEXT_HTML_VALUE to my #GetMapping annotation then I get 406 Not Acceptable error response (but no exception in my spring app)...
How to fix it?

I'm not sure why you are facing problems when using produces in your mapping.
I gave a quick try and it worked for me.
#GetMapping(value = "test", produces=MediaType.TEXT_HTML_VALUE)
public ResponseEntity<String> test(#RequestParam("filename") String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get("/tmp/generated/" + filename)), "UTF-8");
return new ResponseEntity<String>(content, HttpStatus.OK);
}
Tested in Chrome browser:
File
NOTE: I tested this controller using SpringBoot v2.0.5.RELEASE
Cheers!

I have successfully build application with Spring Boot 1.5.2.RELEASE and it will return static HTML site from any directory
you can checkout here

Related

Is there a change to return a png file like response.png instead of response.bin

The aim of my code is to retrieve an image from a third-party service.
I struggled a little for endpoint of download to work and only partially succeeded. When I call the endpoint via postman the answer is a .bin file, but what I need is to have a .png file. The greatest success is being able to get a .png file being able to customize the name as well. But personalization of the is not strictly necessary.
The project is built with the initializer and has the following dependencies:
spring-boot-starter-web;
lombok
spring-boot-starter-webflux
reactor-spring
Below is the source code of my endpoint:
#GetMapping("/retrieve-image")
public Mono<byte[]> retrieveImage(ImageRequest request) throws ExecutionException, InterruptedException, IOException {
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
queryParams.add("attribute", request.getAttribute()); // fake for query string setting.
Mono<byte[]> image = webClient
.get()
.uri(uriBuilder -> uriBuilder
.path(Endpoint.THIRD_PARTY_SERVICE_URI)
.queryParams(queryParams)
.build())
.accept(MediaType.valueOf(String.valueOf(MediaType.IMAGE_PNG)))
.exchange()
.flatMap(clientResponse -> clientResponse.bodyToMono(byte[].class)
.doOnSuccess(body -> {
if (clientResponse.statusCode().isError()) {
log.error("HttpStatusCode = {}", clientResponse.statusCode());
log.error("HttpHeaders = {}", clientResponse.headers().asHttpHeaders());
log.error("ResponseBody = {}", body);
}
}));
return image;
}
You can also add the mime type of the file to the produces section of the #GetMapping annotation, it should look something like this:
#GetMapping(path = "/retrieve-image",
produces = "image/png")
Additionally, instead of returning a Mono<byte[]>, you can wrap your response in a ResponseEntity<Resource>. This gives you the possibility to add Headers and tell the browser the name of your file. For example:
HttpHeaders header = new HttpHeaders();
header.add(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=image.png");
header.add("Access-Control-Expose-Headers", "Content-Disposition");
return ResponseEntity.ok().
.headers(header)
.contentLength(Files.size(path))
.body(<<YOUR_FILE_HERE>>);
One last thought: If you add both spring-boot-starter-web and spring-boot-starter-webflux to your dependencies, the app will work, but it doesn't use Netty from Webflux, instead the usual Tomcat. So you don't benefit from the reactive features.

POST byte array in multipart using Spring RestTemplate

I'm trying to POST a multipart/form-data using Spring RestTemplate with a byte array as the file to upload and it keeps failing (Server rejects with different kinds of errors).
I'm using a MultiValueMap with ByteArrayResource. Is there something I'm missing?
Yes there is something missing.
I have found this article:
https://medium.com/#voziv/posting-a-byte-array-instead-of-a-file-using-spring-s-resttemplate-56268b45140b
The author mentions that in order to POST a byte array using Spring RestTemplate one needs to override getFileName() of the ByteArrayResource.
Here is the code example from the article:
private static void uploadWordDocument(byte[] fileContents, final String filename) {
RestTemplate restTemplate = new RestTemplate();
String fooResourceUrl = "http://localhost:8080/spring-rest/foos"; // Dummy URL.
MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
map.add("name", filename);
map.add("filename", filename);
// Here we
ByteArrayResource contentsAsResource = new ByteArrayResource(fileContents) {
#Override
public String getFilename() {
return filename; // Filename has to be returned in order to be able to post.
}
};
map.add("file", contentsAsResource);
// Now you can send your file along.
String result = restTemplate.postForObject(fooResourceUrl, map, String.class);
// Proceed as normal with your results.
}
I tried it and it works!
I added an issue to send a request from java client to Python service in FastApi and sending a ByteArrayResource instaead of simple byte[] fixed the issue.
FastAPI server returned: "Expected UploadFile, received: <class 'str'>","type":"value_error""

Encoding for downloaded files in Spring

I want to create a controller which will sent to client a CSV file, and I created the next controller:
#ResponseStatus(value = HttpStatus.OK)
#RequestMapping(value = "/csv", method = RequestMethod.GET)
public ResponseEntity downloadCsvAllInvoiceTransactionsByFilter(
#PageableDefault(direction = DESC, sort = "invoiceDate", size = 30) Pageable pageRequest) throws IOException {
String someVariable = "Some text";
byte[] out = someVariable.getBytes(Charset.forName("UTF-8"));
HttpHeaders responseHeaders = new HttpHeaders();
LOGGER.info(new String(out));
responseHeaders.add("content-disposition", "attachment; filename=transactions.csv" );
responseHeaders.add("Content-Type","text/csv; charset=utf-8");
return new ResponseEntity<>(out,responseHeaders,HttpStatus.OK);
}
Logger is displaying the correct string:
Some text
but in downloaded file there is another one
U29tZSB0ZXh0
How can I fix this?
Body of ResponseEntity goes through a message converter before it gets sent. The choice of the particular converter depends on class of the body and response and request headers.
I tried to reproduce the issue with your code snippet and got expected text in csv file. So I assume that you got a message converter registered that converts byte arrays the way you observe.
You can debug AbstractMessageConverterMethodProcessor#writeWithMessageConverters and see which converter is chosen and why.

How to return image in Spring REST to browser

I need to return image in my Spring controller.
I try answer in this Spring MVC: How to return image in #ResponseBody? but it's not working
my code is like this
#RequestMapping(value = "cabang/photo", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<byte[]> getPhoto() throws IOException {
File imgPath = new File("D:\\test.jpg");
byte[] image = Files.readAllBytes(imgPath.toPath());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_JPEG);
headers.setContentLength(image.length);
return new ResponseEntity<>(image, headers, HttpStatus.OK);
}
but when I access it in browser, it doesn't show anything (just no picture icon). But if I read the image byte array, it is not empty.
Do I miss anything in my code?
Your code looks ok. Make sure you added ByteArrayHttpMessageConverter to your application's list of http message converters.
Java Config :
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
ByteArrayHttpMessageConverter byteConverter = new ByteArrayHttpMessageConverter();
converters.add(byteConverter);
super.configureMessageConverters(converters);
}

Spring MVC with ajax file upload and MultipartFile

I have an issue using Ajax upload with Spring 3 MVC. I understand that I have to configure multipartResolver bean in spring config, which I've done. Than I can have controller like this
#RequestMapping(value ="/settingsSim")
#ResponseBody
public Map uploadSimSettings(#RequestParam(value="qqfile", required=true) MultipartFile settings) {
Map<String, Object> ret = new HashMap<String, Object>();
return ret;
}
The problem is that when I actually send the request to the server (actually valums Ajax file upload does this for me), I get an Internal server error response and nothing is shown in the logs. I am really scratching my head now, as I cannot figure out the problem.
my solution:
#RequestMapping(value = "/create/upload", method = RequestMethod.POST, consumes="multipart/form-data", produces="application/json")
#ResponseBody()
public String handleImageUpload(#RequestParam(value="qqfile", required=true) MultipartFile[] files,
#ModelAttribute(value="files") List<MultipartFile> filesSession) throws IOException, FileUploadException {
if (files.length > 0) {
filesSession.addAll(Arrays.asList(files));
// store the bytes somewhere
return "{\"success\": true}";
}
else {
return "{\"success\": false}";
}
}
#RequestMapping(value = "/create/upload", method = RequestMethod.POST, consumes="application/octet-stream", produces="application/json")
#ResponseBody()
public String handleImageUploadApplication(HttpServletRequest request,
#ModelAttribute(value="files") List<MultipartFile> filesSession) throws IOException, FileUploadException {
if (request.getInputStream() != null) {
// creamos el fichero temporal
File file = File.createTempFile("file", "valumns",
RepositoryData.getRepositoryData());
FileOutputStream fos = new FileOutputStream(file);
// copiamos contenido
Streams.copy(request.getInputStream(), fos, true);
//TODO:
//filesSession.addAll(Arrays.asList(files));
// store the bytes somewhere
return "{\"success\": true}";
}
else {
return "{\"success\": true}";
}
}
#ExceptionHandler(Exception.class)
#ResponseStatus(value = HttpStatus.SERVICE_UNAVAILABLE)
public void handleException(Exception ex) {
log.error("Ocurrio un error en el album", ex);
}
I had the same problem with the fineuploader (valums), and I tried using request.getInputStream() but did not get it to work.
The #ResponseBody annotation worked but I got the whole body with headers. I thought processing that and stripping off the unwanted chunks was not very elegant.
I looked further and found the solution is this post:
problem with spring ajax file upload
Like it is said, I added the bean configuration for the multipart resolver to my spring configuration
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
After that, I could easily retrieve my file using
public #ResponseBody Map ajaxUploadFile(#RequestParam MultipartFile qqfile) { ... }
Don't forget to add the Apache commons-io.jar and commons-fileupload.jar libraries in your project to get it to work
When using valums plugin I solved this problem by using #RequestBody Spring annotation.
You could rewrite your code as follows:
#RequestMapping(value ="/settingsSim",method=RequestMethod.POST)
#ResponseBody
public Map uploadSimSettings(#RequestBody String body) {
/*
some controller logic
*/
}
Note that the variable body will contain the contents of the uploaded file. Also there is no method declaration in your example which means that your method will be mapped to GET request.
P.S. I also had this "no multipart boundary" problem when parsing request with Apache Commons. HttpServletRequest#getParts() returns just an empty collection.
#Tomas I encountered same issue while using the same jquery plugin. Please change the Content-Type in the plugin code to xhr.setRequestHeader("Content-Type", "multipart/form-data"); on my plugin its line 1203, after this its now showing a stack trace, however I am encountering another issue where the logs are printing :
Sep 8, 2011 9:43:39 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet dispatcher threw exception
org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
As per my observation the file upload plugin does not send a multipart file but sends a stream. I could get it to work by declaring the controller method to accept filename as request param qqfile and the second parameter as httprequest. I then did further processing using request.getinputstream. Hope that helps!
Regards,
Pradyumna

Resources