【问题标题】:Spring + JPA + WebMVC: Returning a JPEGSpring + JPA + WebMVC:返回 JPEG
【发布时间】:2015-01-08 14:41:51
【问题描述】:

我有一个 Spring 应用程序,它使用带有 WebMVC 的 JPA 存储库,我正在尝试扩展它以支持图像。我可以上传图像并将它们存储在服务器端,但是在使用客户端实际检索图像时,我实际上无法成功发送响应。

首先,这是我公开的客户端 API:

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

这是我第一次尝试实现:

@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;
}

但是,这种方法在测试时会产生以下异常:

javax.servlet.ServletException:无法解析名称为“dispatcherServlet”的 servlet 中名称为“extra/1/image”的视图

看了一会,我想我可能需要@ResponseBody注解以及如下:

@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
}

但是,添加 @ResponseBody 与我拥有的工作视频示例(使用 Spring,但不使用 JPA 存储库或 WebMVC)冲突,并产生以下异常:

org.springframework.web.HttpMediaTypeNotAcceptableException: 找不到可接受的表示

除了使用 Response 返回值之外,我还尝试过返回 FileSystemResource,但这会产生如下 JSON 错误:

预期为 BEGIN_OBJECT,但在第 1 行第 1 列为 STRING

由于我只是想返回一个图像,我认为不需要 JSON,但我不知道如何删除 JSON 标头信息,因为上面的 producessetContentType显然没有任何影响。此外,由于图像可能很大,我认为@Streaming 注释是有保证的,并且只能与Response 一起使用。

如果有帮助,这里是我用来测试我的应用程序的代码:

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));

我已经在这几天了,我还没有找到任何可以建议如何克服上述问题的东西。我认为使用带有 WebMVC 的 JPA 存储库来控制对静态文件存储的访问经常出现,但我还没有找到任何有用的东西。任何帮助将不胜感激。

谢谢

【问题讨论】:

    标签: java spring spring-mvc jpa


    【解决方案1】:

    尝试在 Spring MVC 设置中将 ByteArrayHttpMessageConverter 添加到转换器,并将方法的返回类型更改为 byte[]。另外不要忘记添加 @ResponseBody 注释。

    【讨论】:

    • 不幸的是,这导致我在使用 FileSystemResource 方法时遇到的相同问题;即: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1. 但是,它确实清除了服务器端的异常。要注册 Bean,除了:@Bean public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter () {...} 之外,我的服务器中是否需要其他任何东西
    • 您还应该在客户端添加相同的转换器
    • 感谢您的建议。我对 Spring 还是很陌生,所以我不确定如何在客户端的默认值之外添加额外的转换。为了清楚起见,我在link 发布了一个单独的问题。
    • 看来您正在为客户端使用 Retrofit 库。您可以尝试将其添加到您的界面 @Headers({"Content-Type: image/jpeg"})。之后,您可以使用 response.getBody().in() 获取输入流。然后您可以使用一些辅助方法将此 InputStream 转换为字节数组。您可以通过改造文件支持访问此链接。 (blog.pawelhajduk.net/retrofit-file-support)。
    • 我建议你的服务器端控制器应该返回 byte[]。如果您将 Content-Type: image/jpeg 添加到客户端,那么您还应该在服务器端设置此标头,如 Paul Vargas 上面提到的。如果你没有在服务器端设置它,它应该有一些默认的内容类型,如 application/octet-stream 或类似的东西。(你可以在浏览器上使用类似 Firebug 的工具查看响应标头) .如果客户端与服务器端发送的标头匹配,它应该能够处理响应。
    【解决方案2】:

    您可以尝试以下方法:

    @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);
    }
    

    如您所见,您需要一个图像字节数组。但是,您也可以使用流。请参阅Return a stream with Spring MVC's ResponseEntity

    【讨论】:

    • 不幸的是,这会导致我在使用 FileSystemResource 方法时遇到的相同问题;即:java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1. 但是,它确实清除了服务器端的异常。
    • 我刚刚意识到问题可能不再出现在服务器端,而是在您提出建议后出现在客户端(顺便感谢 InputStreamResource 讨论的链接)。那么,我认为客户需要对RestAdapter.Builder.setConverter 做一些事情,对吗?
    • 您在测试中使用的是哪个客户端库?
    • 我只是在使用实现 API 的 Apache HttpClient。你问的是这个吗?如果有帮助,并且您不介意看一下,我可以简单地打包服务器和测试项目供您查看。
    • 我觉得this更简单。 :)
    【解决方案3】:

    好吧,这只是我能想到的解决问题的最愚蠢的方法,我不认为这是一个真正的答案,但这是我唯一可以开始工作的方法。出于某种原因,我的设置似乎绝对需要一个持久化实体才能不出现 JSON 错误。在这种情况下,我围绕List&lt;Byte&gt; 构建了一个包装器(似乎即使是一个字节[] 也不够好——它需要是可以用@ElementCollection 注释的东西)。返回图像时,我将图像内容复制到刚才提到的包装器中;在客户端,我提取存储的字节。然后我删除了持久化的图像数据。

    我知道这在所需开销方面过于密集,并且可能无法在现实环境中工作,但正如我所说,这就是我所拥有的。这是难以置信的愚蠢,但我已经放弃尝试找到正确的解决方案。

    顺便说一句,如果有人想看看对正确解决方案的原始尝试(没有上面提到的图像包装器),可以使用here

    【讨论】:

      猜你喜欢
      • 2014-07-08
      • 2020-02-25
      • 1970-01-01
      • 1970-01-01
      • 2018-07-15
      • 2015-01-13
      • 2020-03-26
      • 1970-01-01
      • 2012-11-12
      相关资源
      最近更新 更多