简体   繁体   中英

Spring + JPA + WebMVC: Returning a JPEG

I have a Spring application that uses a JPA repository with WebMVC, and I'm trying to extend it for image support. I can upload images and store them server-side, but when it comes to actually retrieving the image with a client, I cannot actually successfully send a response.

First, this is the client API that I expose:

@Streaming
@GET(PATIENT_EXTRA_PATH + "/{id}" + GET_IMAGE_RELPATH)
public Response getImageData(@Path(ID) long id, @Query(IMAGE_FILE) String imageFile);

Here is my first attempt at an implementation:

@RequestMapping(value = PainManagementSvcApi.PATIENT_EXTRA_PATH + "/{id}" +
        PainManagementSvcApi.GET_IMAGE_RELPATH, method = RequestMethod.GET,
        produces = MediaType.IMAGE_JPEG_VALUE)
public HttpServletResponse getImageData(@PathVariable(PainManagementSvcApi.ID) long id,
        @RequestParam(PainManagementSvcApi.IMAGE_FILE) String imageFile,
        Principal principal, HttpServletResponse response) {
    // Do some stuff to ensure image availability and access

    // All of this works
    response.setContentType("image/jpeg");
    imageFileManager.copyImageData(imageFile, response.getOutputStream());
    response.setStatus(HttpServletResponse.SC_OK);

    // Return the response
    return response;
}

However, that approach produces the following exception when testing:

javax.servlet.ServletException: Could not resolve view with name 'extra/1/image' in servlet with name 'dispatcherServlet'

After looking around for a bit, I thought that I perhaps needed the @ResponseBody annotation as well as follows:

@RequestMapping(value = PainManagementSvcApi.PATIENT_EXTRA_PATH + "/{id}" +
        PainManagementSvcApi.GET_IMAGE_RELPATH, method = RequestMethod.GET,
        produces = MediaType.IMAGE_JPEG_VALUE)
public HttpServletResponse getImageData(@PathVariable(PainManagementSvcApi.ID) long id,
        @RequestParam(PainManagementSvcApi.IMAGE_FILE) String imageFile,
        Principal principal, HttpServletResponse response) {
    // Same code as before
}

However, adding @ResponseBody conflicts with a working video example that I have (which uses Spring, but does not use a JPA repository or WebMVC), and produces the following exception:

org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation

In addition to using a Response return value, I've also tried returing a FileSystemResource instead, but that produces JSON errors like the following:

Expected BEGIN_OBJECT but was STRING at line 1 column 1

Since I'm just trying to return an image, I would wouldn't think that JSON is needed, but I can't figure out how to remove the JSON header information, since the produces and setContentType above apparently don't have any impact. Additionally, since the images can potentially be large, I'd think that the @Streaming annotation is warranted, and that can only be used with a Response .

If it helps, here is the code that I've been using to test my application:

Response response = user.getImageData(extra.getId(), fileName);
assertEquals(HttpStatus.SC_OK, response.getStatus());
InputStream imageStream = response.getBody().in();
byte[] retrievedFile = IOUtils.toByteArray(imageStream);
byte[] originalFile = IOUtils.toByteArray(new FileInputStream(images[index++]));
assertTrue(Arrays.equals(originalFile, retrievedFile));

I've been at this, now, for a few days, and I've not found anything that would suggest how to overcome my problems above. I would think that using a JPA repository with WebMVC to gate access to a static file store comes up often, but I've yet to find anything useful. Any help would be appreciated.

Thanks

Try adding ByteArrayHttpMessageConverter to converters in Spring MVC setup and change the return type of the method to byte[] . Also do not forget to add @ResponseBody annotation.

You can try the following:

@RequestMapping(value = PATIENT_EXTRA_PATH + "/{id}" + GET_IMAGE_RELPATH, 
                method = RequestMethod.GET)
public ResponseEntity<byte[]> getImageData(@PathVariable(PainManagementSvcApi.ID) long id,
        @RequestParam(PainManagementSvcApi.IMAGE_FILE) String imageFile,
        Principal principal, HttpServletResponse response) {

    // Do some stuff to ensure image availability and access

    response.setContentType("image/jpeg");

    // image to byte array
    byte[] contents = imageFileManager.copyImageData(imageFile);

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.parseMediaType("image/jpeg"));
    return new ResponseEntity<byte[]>(contents, headers, HttpStatus.OK);
}

As you can see, you need an array of bytes of the image. However, you can also use a stream. See Return a stream with Spring MVC's ResponseEntity .

Alright, this is just about the dumbest way I can think of to solve my problem, and I don't think it's a real answer, but it's the only thing I can get to work. For some reason, my setup seems to absolutely require a persisted entity in order for JSON errors not to appear. That being the case, I built a wrapper around a List<Byte> (it seems that even a byte[] wasn't good enough -- it needed to be something that could be annotated with @ElementCollection ). When returning an image, I copy the image contents into the wrapper I just mentioned; on the client end, I extract the bytes stored. I then erase the persisted image data.

I know that this is excessively intensive in terms of the overhead required, and probably won't work in a realistic setting, but, as I said, it's all I've got. It's stupid beyond all belief, but I've given up on trying to find a correct solution.

By the way, if anyone would like to take a look at the original attempts at a correct solution (sans the Image wrapper mentioned above), that is available here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM