I have a question that makes my head ache.
First, my project structure looks like below.
I made a controller, which returns image(*.png) file to the appropriate request.
The code of controller is written below.
#Controller
public class ImageController {
#GetMapping(value = "/ImageStore.do", produces = MediaType.IMAGE_PNG_VALUE)
public #ResponseBody byte[] getStoreImage(HttpServletRequest request) throws IOException {
String image_name = request.getParameter("image_name");
Resource resource = null;
try {
resource = new ClassPathResource("/images/stores/" + image_name);
if(resource == null) {
throw new NullPointerException();
}
} catch(NullPointerException e) {
resource = new ClassPathResource("/images/stores/noimage.png");
}
InputStream inputStream = resource.getInputStream();
return IOUtils.toByteArray(inputStream);
}
}
Q1. I added try-catch phrase to send noimage.png if the request parameter is wrong, or if the filename of request parameter image_name does not exist. But it doesn't seem to work, and it gives me log saying
class path resource [images/stores/noima.png] cannot be opened because it does not exist
(If you need to know the full stack trace, I will comment below.)
Q2. I have 2 image files, hello.png and noimage.png in the folder /resources/images/stores/. I can read noimage.png correctly, but if I make request localhost:8080/ImageStore.do?image_name=hello.png, then it makes an error, making the same log in Q1.
There's no reason to think that the constructor would result in a null value.
The exception you are getting is likely from the getInputStream method, which is documented to throw
FileNotFoundException - if the underlying resource doesn't exist
IOException - if the content stream could not be opened
A slight adjustment might help
#Controller
public class ImageController {
#GetMapping(value = "/ImageStore.do", produces = MediaType.IMAGE_PNG_VALUE)
public #ResponseBody byte[] getStoreImage(HttpServletRequest request) throws IOException {
InputStream is = null;
try {
String image_name = request.getParameter("image_name");
is = new ClassPathResource("/images/stores/" + image_name).getInputStream();
} catch(FileNotFoundException e) {
is = new ClassPathResource("/images/stores/noimage.png").getInputStream();
}
return IOUtils.toByteArray(is);
}
}
You should include the stack trace, and exception message, which might assist understanding your second query, but I would check that the file really does exist, with the exact name you're using.
Related
Goodmorning everyone,
I am creating a web application for my degree and I have been running into a problem for several days related to the loading and retrive of images from the database.
I can upload the photos without major problems, I still leave the controller code:
#PostMapping()
public AckDto handleImagePost(#RequestParam ("file") MultipartFile file, #RequestParam Long userId, #RequestParam Long contestId) throws ServiceException, IOException {
PhotoDto photoDto = new PhotoDto();
photoDto.setTitle("Test Image");
photoDto.setDescription("Test description");
photoDto.setImage(file.getBytes());
return photoService.saveImagineFile(photoDto, userId, contestId);
}
While for the get of the photos I can only take one photo with the following method:
#GetMapping(value = "/image", produces = MediaType.IMAGE_PNG_VALUE)
public Resource downloadImage(#RequestParam Long contestId) throws ServiceException, IOException {
Photo photo = photoService.findByContest_Id(contestId);
byte[] image = photoService.findByContest_Id(contestId).getImage();;
return new ByteArrayResource(image);
}
while if I try to take more photos with the following code obviously changing the Service and Repository:
#GetMapping(value = "/image", produces = MediaType.IMAGE_PNG_VALUE)
public List<Resource> downloadImage(#RequestParam Long contestId) throws ServiceException, IOException {
List<Photo> photo = photoService.findByContest_Id(contestId);
List<Resource> results = new ArrayList<>();
for(Photo p : photo){
results.add(new ByteArrayResource(p.getImage()));
}
return results;
}
it returns me the following errors:
org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class java.util.ArrayList] with preset Content-Type 'null'
I also tried to return a PhotoDto list but it doesn't change anything,
Some idea?
If you need any other class, ask and it will be given to you.
Thanks to everyone, I solved the problem was that I had accidentally left some old notes that I assume were in conflict with others.
Not sure if the following works for you, but you can zip all images and then retrieve them in a unique file.
The code example (taken from the reference below):
#GetMapping(value = "/zip-download", produces="application/zip")
public void zipDownload(#RequestParam List<String> name, HttpServletResponse response) throws IOException {
ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream());
for (String fileName : name) {
FileSystemResource resource = new FileSystemResource(fileBasePath + fileName);
ZipEntry zipEntry = new ZipEntry(resource.getFilename());
zipEntry.setSize(resource.contentLength());
zipOut.putNextEntry(zipEntry);
StreamUtils.copy(resource.getInputStream(), zipOut);
zipOut.closeEntry();
}
zipOut.finish();
zipOut.close();
response.setStatus(HttpServletResponse.SC_OK);
response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + zipFileName + "\"");
}
Reference: https://www.devglan.com/spring-boot/spring-boot-file-upload-download
Rather than returning a List, return a ResponseEntity<List>.
It MIGHT work.
Problem is that whatever way you're going about it, it's unlikely the frontend will be able to pull out your images.
UUEncoding them manually and putting them in the HTTP headers is probably your best bet. That way the frontend can iterate over the headers containing the images and get them one by one in a format it should be able to decode.
Background
here is the method defined in #RestController, it reads file from disk then stream back.
#RequestMapping(value = "/bill", method = RequestMethod.GET)
public ResponseEntity<Object> getbill(){
...
InputStream in = new FileInputStream(file);
InputStreamResource inputStreamResource = new InputStreamResource(in);
httpHeaders.setContentLength(file.Length());
return new ResponseEntity(inputStreamResource, httpHeaders, HttpStatus.OK);
}
Issue
I would like to delete the file after request is served, but unable to find a good place.
I would assume it should be after inputStream gets closed (https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-web/src/main/java/org/springframework/http/converter/ResourceHttpMessageConverter.java#L117) . it can not be done in above method since file is opened by Inputstream.
Answer Summary
Thank you all for helping with this.
The accepted answer requires least change and working well.
Aside from the fact that it is bad practice in a RESTfull service to perform destructive operations on GET requests this can not be done by the default Java libraries. The more widely accepted implementation would be a GET that streams the file followed by a DELETE call to remove the file.
But you can do it by implementing your own InputStream, see an earlier thread in Stackoverflow on deleting files on closing a InputStream.
Assuming that you are creating the file in the same controller.
You can use:
try (BufferedWriter out = Files
.newBufferedWriter(newFilePath, Charset.defaultCharset(),
StandardOpenOption.DELETE_ON_CLOSE)) {
InputStream in = new FileInputStream(newFilePath.toFile());
InputStreamResource inputStreamResource = new InputStreamResource(in);
httpHeaders.setContentLength(file.Length());
return new ResponseEntity(inputStreamResource, httpHeaders, HttpStatus.OK);
} catch (Exception e) {
}
As the BufferedWriter will close on return, the file will get deleted.
Based on #phlogratos's answer, you can try like this.
#GetMapping("/download")
public ResponseEntity<InputStreamResource> download() throws Exception {
... codes ...
InputStreamResource isr = new InputStreamResource(new FileInputStream(file) {
#Override
public void close() throws IOException {
super.close();
boolean isDeleted = file.delete();
logger.info("export:'{}':" + (isDeleted ? "deleted" : "preserved"), filename);
}
});
return new ResponseEntity<>(isr, respHeaders, HttpStatus.OK);
}
Extend FileInputStream with your own implementation and then overwrite close. When the input stream is closed, your file gets deleted as well.
public class MyFileInputStream extends FileInputStream {
private final File myFile;
public MyFileInputStream(File file) throws FileNotFoundException {
super(file);
myFile = file;
}
#Override
public void close() throws IOException {
super.close();
myFile.delete();
}
}
I'm trying to implement a project that will basically serve static files that are located in a folder of the project. I'm using Jersey 2.22.1. It should be a method that receives a filename and answers with the requested file.
Implementation:
#GET
#Path("/{filename : .*}")
public Response getFile(#PathParam("filename") String filename) {
String f = getFileFromSomewhere(filename);
StreamingOutput fileStream = new StreamingOutput()
{
#Override
public void write(java.io.OutputStream output) throws IOException, WebApplicationException
{
try
{
java.nio.file.Path path = Paths.get(f);
byte[] data = Files.readAllBytes(path);
output.write(data);
output.flush();
}
catch (Exception e)
{
throw new WebApplicationException("File Not Found !!");
}
}
};
return Response
.ok(fileStream)
.build();
}
The following error occurs:
GRAVE: Error while closing the output stream in order to commit response.
java.lang.IllegalStateException: getWriter() has already been called for this response
at org.apache.catalina.connector.Response.getOutputStream(Response.java:628)
The files are being requested by a browser and i have noticed that the error only occurs with .js files (it's ok with.html and .css files). There are reasons why this files should be served by this method and not by the conventional mode.
Any ideas?
I've tried the various ways given in Stackoverflow, maybe I missed something.
I have an Android client (whose code I can't change) which is currently getting an image like this:
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
Where url is the location of the image (static resource on CDN). Now my Spring Boot API endpoint needs to behave like a file resource in the same way so that the same code can get images from the API (Spring boot version 1.3.3).
So I have this:
#ResponseBody
#RequestMapping(value = "/Image/{id:.+}", method = RequestMethod.GET, consumes = MediaType.ALL_VALUE, produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<byte[]> getImage(#PathVariable("id")String id) {
byte[] image = imageService.getImage(id); //this just gets the data from a database
return ResponseEntity.ok(image);
}
Now when the Android code tries to get http://someurl/image1.jpg I get this error in my logs:
Resolving exception from handler [public
org.springframework.http.ResponseEntity
com.myproject.MyController.getImage(java.lang.String)]:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not
find acceptable representation
Same error happens when I plug http://someurl/image1.jpg into a browser.
Oddly enough my tests check out ok:
Response response = given()
.pathParam("id", "image1.jpg")
.when()
.get("MyController/Image/{id}");
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
byte[] array = response.asByteArray(); //byte array is identical to test image
How do I get this to behave like an image being served up in the normal way? (Note I can't change the content-type header that the android code is sending)
EDIT
Code after comments (set content type, take out produces):
#RequestMapping(value = "/Image/{id:.+}", method = RequestMethod.GET, consumes = MediaType.ALL_VALUE)
public ResponseEntity<byte[]> getImage(#PathVariable("id")String id, HttpServletResponse response) {
byte[] image = imageService.getImage(id); //this just gets the data from a database
response.setContentType(MediaType.IMAGE_JPEG_VALUE);
return ResponseEntity.ok(image);
}
In a browser this just seems to give a stringified junk (byte to chars i guess). In Android it doesn't error, but the image doesn't show.
I believe this should work:
#RequestMapping(value = "/Image/{id:.+}", method = RequestMethod.GET)
public ResponseEntity<byte[]> getImage(#PathVariable("id") String id) {
byte[] image = imageService.getImage(id);
return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).body(image);
}
Notice that the content-type is set for ResponseEntity, not for HttpServletResponse directly.
Finally fixed this... I had to add a ByteArrayHttpMessageConverter to my WebMvcConfigurerAdapter subclass:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
final ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
final List<MediaType> list = new ArrayList<>();
list.add(MediaType.IMAGE_JPEG);
list.add(MediaType.APPLICATION_OCTET_STREAM);
arrayHttpMessageConverter.setSupportedMediaTypes(list);
converters.add(arrayHttpMessageConverter);
super.configureMessageConverters(converters);
}
In case you don't know the file/mime type you can do this.... I've done this where i take an uploaded file and replace the file name with a guid and no extension and browsers / smart phones are able to load the image no issues.
the second is to serve a file to be downloaded.
#RestController
#RequestMapping("img")
public class ImageController {
#GetMapping("showme")
public ResponseEntity<byte[]> getImage() throws IOException{
File img = new File("src/main/resources/static/test.jpg");
return ResponseEntity.ok().contentType(MediaType.valueOf(FileTypeMap.getDefaultFileTypeMap().getContentType(img))).body(Files.readAllBytes(img.toPath()));
}
#GetMapping("thing")
public ResponseEntity<byte[]> what() throws IOException{
File file = new File("src/main/resources/static/thing.pdf");
return ResponseEntity.ok()
.header("Content-Disposition", "attachment; filename=" +file.getName())
.contentType(MediaType.valueOf(FileTypeMap.getDefaultFileTypeMap().getContentType(file)))
.body(Files.readAllBytes(file.toPath()));
}
}
UPDATE in java 9+ you need to add compile 'com.sun.activation:javax.activation:1.2.0' to your dependencies this has also been moved or picked up by jakarta.see this post
Using Apache Commons, you can do this and expose the image on an endpoint
#RequestMapping(value = "/image/{imageid}",method= RequestMethod.GET,produces = MediaType.IMAGE_JPEG_VALUE)
public #ResponseBody byte[] getImageWithMediaType(#PathVariable int imageid) throws IOException {
InputStream in = new ByteArrayInputStream(getImage(imageid));
return IOUtils.toByteArray(in);
}
All images will be served at endpoint /image/{imageid}
I'm trying to create some REST web services with Java in order to send data, do calculations on the server, and return the result. In a first stage I send and receive information as an excel file (in the future I prefer to use XML or JSON).
Well, after a lots of hours trying it, and reading lots of posts, it seems I'm very close to achieve it, but I don't know how to obtain the final response of the server.
I have a service like this:
#GET
#Path("/test")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getFile(#QueryParam("IDfile") String IDfile) {
if(IDfile.trim().length() == 0 || IDfile == null) {
return Response.status(Response.Status.BAD_REQUEST).entity("IDfile cannot be blank").build();
}
String uploadedFileLocation = "C:\\FilesWebservice\\" + IDfile;
Boolean sortida = false;
try {
prova prueba = new prova();
sortida = prueba.prova(uploadedFileLocation); //this creates an xls file as response
} catch (Exception ex) {
System.out.println("error" + ex.toString());
Logger.getLogger(ServiceResource.class.getName()).log(Level.SEVERE, null, ex);
}
if (sortida) {
File file = new File("C:\\FilesWebservice\\out\\prediction.xls"); // the File path you want to serve.
return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"" ) //optional
.build();
} else
return Response.status(500).entity("It was unable to calculate (Ask God for the reason)").build();
}
It works OK, if I send a GET through the browser I receive the file in my downloads folder, but I need to consume the service with another application. Thus, I'm developing a client with Netbeans, and then, NB created automatic code according to my web service. In this case I have:
public <T> T getFile(Class<T> responseType, String IDfile) throws ClientErrorException {
WebTarget resource = webTarget;
if (IDfile != null) {
resource = resource.queryParam("IDfile", IDfile);
}
resource = resource.path("test");
Builder builder = resource.request(MediaType.APPLICATION_OCTET_STREAM_TYPE);
Invocation invocation = builder.buildGet();
return resource.request(MediaType.APPLICATION_OCTET_STREAM_TYPE).get(responseType);
}
Maybe I added some lines, I can't remember now. Anyway, the service returns a status code, a customised message and the file as attachment. I want to read at least the status code and obviously save the file, but I don't know how can I do it.
I tried to do:
MyJerseyClientAlgA client = new MyJerseyClientAlgA("192.168.1.30");
Object response = client.getFile(Response.class, "3cphkhfu.xls");
but it was unsuccessful to extract the information I need from 'response'.
Any help or ideas would be appreciated.
Many thanks in advance
EDIT:
Thanks #LutzHorn for your reply. I'm not sure if I understand well your proposal, I'll do some tests and if I find a solution I'll post under my question. Anyway, I generated again the automatic code for consuming the REST service, that is:
public <T> T getFile(Class<T> responseType, String IDfile) throws ClientErrorException {
WebTarget resource = webTarget;
if (IDfile != null) {
resource = resource.queryParam("IDfile", IDfile);
}
resource = resource.path("test");
return resource.get(responseType);
}
but I have an error in the last line, it indicates:
cannot find symbol
symbol: method get(Class)
so I changed this line for
return resource.request(MediaType.APPLICATION_OCTET_STREAM_TYPE).get(responseType);
but I'm not sure if this is right.
Well, after some hours searching and testing, this piece of code works. I don't know if it is the best solution, but it does exactly what I want: extract the status and save the file returned by the web service.
public void getFile(String IDfile) throws ClientErrorException {
WebTarget resource = webTarget;
if (IDfile != null) {
resource = resource.queryParam("IDfile", IDfile);
}
resource = resource.path("test");
Invocation inv = resource.request(MediaType.APPLICATION_OCTET_STREAM_TYPE).buildGet();
Response rp = inv.invoke();
InputStream attachment = null;
try {
if (rp.getStatus() == 200) {
attachment = rp.readEntity(InputStream.class); //This method can be invoked only once unless you buffer the response...
ReadableByteChannel rbc = Channels.newChannel(attachment); //website.openStream()
FileOutputStream fos = new FileOutputStream("C://FilesWebservice/solution.xls");
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
} else {
System.out.println(rp.getStatus());
}
} catch ( Exception ex) {
ex.printStackTrace();
} finally {
rp.close();
}
}