spring boot serving image/jpeg gives gibberish - spring

I'm trying to serve images from mongodb GridFS. My Controller.
#RequestMapping(value = "{id}", method = RequestMethod.GET)
public void getPhoto (#PathVariable String id, HttpServletResponse response, HttpServletRequest request) {
log.info("#getPhoto > ip of request: " + request.getRemoteAddr() + ", id: " + id);
final InputStream inputStream = resourceService.getMediaResourceById(id);
try {
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
} catch (IOException | NullPointerException e) {
log.error("#getPhoto > error with request for objectId: " + id, e);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
}
The result:
This only happens using Spring Boot. as a test when using Spring and running the exact same code i'm getting:

Writing directly to a response is discouraged in controller methods for various reasons. You are essentially responsible for almost everything yourself. The preferred way is to return something that gets converted as needed.
You already use ResponseEntity<byte[]> now. But your source is a stream and you have to create an unnecessary byte array. You can use Resource instead that wraps all sorts of input streams, be it from files or already opened input streams.
InputStreamResource inputStream = new InputStreamResource(resourceService.getMediaResourceById(id));
return new ResponseEntity<>(inputStream, HttpStatus.OK);
or as of Spring 4.1
return ResponseEntity.ok(inputStream);
Please note that produces = MediaType.IMAGE_JPEG_VALUE doesn't actually set a content type. It's used for content negotiation.

Related

MockMvc Test does not get to the endpoint for a Multipart file in a RestController

I am calling a service in an orders controller which receives a multipart file and processes it and saving it into a database. I am trying to create a Spring Rest Doc for it but it is not even hitting the endpoint. I am creating a list of orders which is what the service expects. It receives the order as a stream as shown and converts into a stream of orders before saving it into a database. I have shown the main part of the controller and my code for generating the rest docs. When I run the code I get the following exception, it never even hits the endpoint when I set a breakpoint. I also used fileupload() but that did not work either.
Exception is:
Content type = application/json
Body = {"path":"/orders/order_reception","exceptionName":
"MissingServletRequestPartException","message":"Required request part 'uploadFile' is not
present",
"rootExceptionName":"MissingServletRequestPartException",
"rootMessage":"MissingServletRequestPartException: Required request part 'uploadFile' is not present"}
#RestController
#RequestMapping(value = "/orders")
#Validated
class OrderController{
#PostMapping(path = "/order_reception")
public ResponseEntity receiveData(#RequestPart MultipartFile uploadFile,
HttpServletRequest request,
HttpServletResponse response) {
if (!uploadFile.isEmpty()) {
try {
Reader reader = new InputStreamReader(request.getInputStream()));
... save file
return new ResponseEntity<>(HttpStatus.HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
#Test
public void sendData() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Order order = repository.getOrder("1233333");
List<Order> orderList = new ArrayList<>():
resourceList.add(order);
MockMultipartFile orderFile = new MockMultipartFile("order-data", "order.json", "application/json",
mapper.writeValueAsString(orderList).getBytes(Charset.defaultCharset()));
mockMvc.perform(multipart("/orders/order_reception")
.file(orderFile))
.andExpect(status().isCreated())
.andDo(document("send-order",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())));
}
Thank you Marten Deinum, your suggestion that the file name was wrong fixed it.
I simply changed name in the MockMultipartFile( "uploadsFile", ...)

Jersey reading zipinputstream [duplicate]

I want to return a zipped file from my server-side java using JAX-RS to the client.
I tried the following code,
#GET
public Response get() throws Exception {
final String filePath = "C:/MyFolder/My_File.zip";
final File file = new File(filePath);
final ZipOutputStream zop = new ZipOutputStream(new FileOutputStream(file);
ResponseBuilder response = Response.ok(zop);
response.header("Content-Type", "application/zip");
response.header("Content-Disposition", "inline; filename=" + file.getName());
return response.build();
}
But i'm getting exception as below,
SEVERE: A message body writer for Java class java.util.zip.ZipOutputStream, and Java type class java.util.zip.ZipOutputStream, and MIME media type application/zip was not found
SEVERE: The registered message body writers compatible with the MIME media type are:
*/* ->
com.sun.jersey.core.impl.provider.entity.FormProvider
What is wrong and how can I fix this?
You are delegating in Jersey the knowledge of how to serialize the ZipOutputStream. So, with your code you need to implement a custom MessageBodyWriter for ZipOutputStream. Instead, the most reasonable option might be to return the byte array as the entity.
Your code looks like:
#GET
public Response get() throws Exception {
final File file = new File(filePath);
return Response
.ok(FileUtils.readFileToByteArray(file))
.type("application/zip")
.header("Content-Disposition", "attachment; filename=\"filename.zip\"")
.build();
}
In this example I use FileUtils from Apache Commons IO to convert File to byte[], but you can use another implementation.
You can write the attachment data to StreamingOutput class, which Jersey will read from.
#Path("/report")
#GET
#Produces(MediaType.TEXT_PLAIN)
public Response generateReport() {
String data = "file contents"; // data can be obtained from an input stream too.
StreamingOutput streamingOutput = outputStream -> {
ZipOutputStream zipOut = new ZipOutputStream(new BufferedOutputStream(outputStream));
ZipEntry zipEntry = new ZipEntry(reportData.getFileName());
zipOut.putNextEntry(zipEntry);
zipOut.write(data); // you can set the data from another input stream
zipOut.closeEntry();
zipOut.close();
outputStream.flush();
outputStream.close();
};
return Response.ok(streamingOutput)
.type(MediaType.TEXT_PLAIN)
.header("Content-Disposition","attachment; filename=\"file.zip\"")
.build();
}
In Jersey 2.16 file download is very easy
Below is the example for the ZIP file
#GET
#Path("zipFile")
#Produces("application/zip")
public Response getFile() {
File f = new File(ZIP_FILE_PATH);
if (!f.exists()) {
throw new WebApplicationException(404);
}
return Response.ok(f)
.header("Content-Disposition",
"attachment; filename=server.zip").build();
}
I'm not sure I it's possible in Jersey to just return a stream as result of annotated method. I suppose that rather stream should be opened and content of the file written to the stream. Have a look at this blog post. I guess You should implement something similar.

How to implement the observer pattern for REST API's?

I'm looking to create a REST API to which clients subscribe to certain data. When the data changes (due to some external event) I want to notify the clients (observers) with the new data.
I want to use Spring for the REST API's, I have no clue how to register and notify the observers though.
Some guidance and or good practises would be very helpful.
Thank you
In spring boot you can register call back urls, an example controller is:
#RestController
public class Controller {
private List<Listener> listeners = new ArrayList<>();
#RequestMapping(value = "/register/{name}", method = RequestMethod.POST)
public ResponseEntity<Void> register(#PathVariable("name") String name, #RequestParam("callbackurl") String callBackUrl) throws Exception {
System.out.println("register, name=" + name + ", callBackUrl=" + callBackUrl);
Listener listener = new Listener(name, URLDecoder.decode(callBackUrl, "UTF-8"));
listeners.add(listener);
System.out.println(listener);
return new ResponseEntity<>(HttpStatus.OK);
}
#RequestMapping(value = "/callback/*", method = RequestMethod.POST)
public ResponseEntity callBack(#RequestBody String message) {
System.out.println("call back with message=" + message);
return new ResponseEntity(HttpStatus.OK);
}
#Scheduled(fixedRate = 10000)
public void notifyListeners() {
System.out.println("notifying listeners");
for (Listener listener : listeners) {
System.out.println("listener " + listener);
CloseableHttpClient client = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(listener.getCallBackUrl());
try {
httpPost.setEntity(new StringEntity("hello listener " + listener));
CloseableHttpResponse response = client.execute(httpPost);
client.close();
} catch (Exception e) {
}
}
}
}
Can be tested like so, register 2 call backs, the URL http://127.0.0.1:8080/callback/app1 is encoded so it can be a paramter.
curl -X POST http://127.0.0.1:8080/register/listener1?callbackurl=http%3A%2F%2F127.0.0.1%3A8080%2Fcallback%2Fapp1
curl -X POST http://127.0.0.1:8080/register/listener1?callbackurl=http%3A%2F%2F127.0.0.1%3A8080%2Fcallback%2Fapp2
In my case for simplicity the client and server are the same application, but they could be different.
You can use Spring 5 with WebFlux. It's a combination of an Iterator and the Observer pattern. The client always gets a new Object, whenever there is one on the server. You can start learning more on that on the Spring documentation pages or on e.g.
New in Spring 5: Functional Web Framework

spring boot HttpServletResponse not setting file name

I have a rest service like this:
import org.apache.tomcat.util.http.fileupload.IOUtils;
#RequestMapping(value = "/xxx", method = GET)
public void getExcel(HttpServletResponse resp) {
resp.setHeader("Content-Disposition", "attachment; filename=\"NAME.xlsx\"");
resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
try (ServletOutputStream outputStream = resp.getOutputStream()) {
IOUtils.copy(A-VALID-FILE-INPUT-STREAM, outputStream);
resp.flushBuffer();
} catch (IOException e) {
throw new AppException(e);
}
}
the problem is that every time I call this service the default save name is 'response', I have tried returning HttpEntity<byte[]>, create objects like HttpHeaders() but nothing changes.
Any help is appreciated
If you are using postman take a look at https://github.com/postmanlabs/postman-app-support/issues/2082
Seems that you will need to wait until this issue will be addressed by postman team.

Couldn't find HttpResponse or HttpServletResponse in Spring 4

Just I am working on web project, In that i need to download a sql file while clicking the link. I tried to find the HttpResponse or HttpServletResponse in controller. Could any one help me to resolve this issue,
#RequestMapping(value = "/downloadFile.htm", method = RequestMethod.GET)
public void toDownloadFile(#RequestParam("fileName") String fileName,
HttpServletResponse response) {
File file = new File(fileName);
if (file != null) {
try {
response.setContentType("application/sql");
// response.setContentLength((new
// Long(file.getLength()).intValue()));
response.setHeader("content-Disposition",
"attachment; filename=" + fileName);
FileCopyUtils.copy(fileName, response.getOutputStream());
} catch (IOException ex) {
LOGGER.error("Exception in toDownloadFile :" + ex);
}
}
}
But in Spring 3 its available, I hope they removed or renamed the HttpServletResponse in Spring 4. Because HttpServeltRequest has been moved to org.springframework.web.context.request.WebRequest. Any one looked into this? Thanks in advance!!!
HttpServeltRequest and HttpServletResponse are javax interfaces not spring.
Are your project dependencies set up correctly?
javax.servlet.http.HttpServletResponse
org.springframework.web.context.request.WebRequest has been around for a while and is documented as...
Generic interface for a web request. Mainly intended for generic web request interceptors, giving them access to general request metadata, not for actual handling of the request.

Resources