【问题标题】:Spring returning image as ResponseEntity<byte[]> - image corruptSpring将图像返回为 ResponseEntity<byte[]> - 图像损坏
【发布时间】:2015-07-09 09:22:58
【问题描述】:

我正在开发一个 spring 3.2.7 应用程序,它通过输出字节数组 ResponseEntity 的 spring 控制器将存储在数据库中的签名作为 base64 字符串发送回用户浏览器。

图像总是损坏,我没有在系统的这部分工作,因为我仔细检查了 svn,并且自从我正在处理的分支创建以来,控制器没有被触及。

我能够将 base64 字符串转换为桌面上的图像,并且我还能够在 spring 介入之前将返回到浏览器的字节数组转换为图像。

下面是我的代码,这显然之前工作过,所以也许有一些配置更改可能导致这种情况?

  @RequestMapping(value = "/submissions/signature/{type}/{id}", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<byte[]> getSignature(@PathVariable String type, @PathVariable Integer id) throws Exception {
   ByteArrayOutputStream baos = new ByteArrayOutputStream();
   String base64 = ... gets from db

   byte[] bytes = Base64.decodeBase64(base64);

    BufferedImage bi = ImageIO.read(new ByteArrayInputStream(bytes));
    ImageIO.write(bi, "png", baos);

    HttpHeaders headers = new HttpHeaders();
    headers.setLastModified(Calendar.getInstance().getTime().getTime());
    headers.setCacheControl("no-cache");
    headers.setContentType(MediaType.IMAGE_PNG);
    headers.setContentLength(baos.toByteArray().length);

    //Image as base64 string is ok in converter
    System.out.println("BASE 64 IMAGE IS: " + base64);
    //This image is created ok on desktop
    FileOutputStream fos = new FileOutputStream("C:\\Users\\p\\Desktop\\test_signature.png");
    fos.write(bytes);
    fos.close();
    //This image is created ok on desktop
    FileOutputStream fos3 = new FileOutputStream("C:\\Users\\p\\Desktop\\test_signature_baos.png");
    fos3.write(bytes);
    fos3.close();

    return new ResponseEntity<byte[]>(baos.toByteArray(), headers, HttpStatus.OK);
   }

图像正在浏览器中呈现,例如:

   <img id="userSignature" width="296" height="110" style="border:0px" src="/webapp/service/submissions/signature/user/${subid}" alt="User signature" />

我没有更改这个类,我被告知它确实有效,我能够从两个字节数组创建图像,它们没问题,看起来一样,我可以渲染签名字符串以进行测试,例如:

  <IMG SRC="data:image/png;base64, <base_64_string>" ALT=""> 

有没有人遇到过类似的问题或知道是什么原因造成的?

我现在尝试从已创建为 png 的文件系统发送图像,但也失败了。

我现在注意到 CSV 文件无法在应用中正确下载,并且它们以相同的方式流式传输:

       @RequestMapping(value = "/results/csv", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<byte[]> getResultsInCsvFormat() throws IOException {

【问题讨论】:

  • 查看您的代码,我完全不确定您为什么要使用ImageIO;您可以直接发送 bytes 作为响应的一部分并完全省略 bibaos,例如return new ResponseEntity&lt;byte[]&gt;(bytes ...)
  • 我实际上不是我的一个同事写的有改变的原因吗?显然 ByteArrayHttpMessageConverter 是自动注册的,因为我在设置该 bean 的配置中没有任何内容。

标签: spring image base64


【解决方案1】:

我已经在InputStream的帮助下成功返回了文件内容:

@RequestMapping(value = "/submissions/signature/{type}/{id}", 
                method = RequestMethod.GET)
public HttpEntity getFile(HttpServletResponse response,
                          @PathVariable String type, 
                          @PathVariable Integer id) {
    String base64 = "foo"; // get base-64 encoded string from db
    byte[] bytes = Base64.decodeBase64(base64);
    try (InputStream inputStream = new ByteArrayInputStream(bytes)) {
        StreamUtils.copy(inputStream, response.getOutputStream());
        response.setContentType(MediaType.IMAGE_PNG_VALUE);
    } catch (IOException e) {
        // handle
    }
    return new ResponseEntity(HttpStatus.OK);
}

请注意,我没有使用ResponseBody,在我的工作版本中,我使用的是MediaType.APPLICATION_OCTET_STREAM_VALUE,而不是实际的文件内容类型。

【讨论】:

  • 我刚刚尝试了您的示例,它可以正常工作并且图像渲染正常,所以.... 看到我没有更改控制器中执行图像下载的任何内容,并且我没有更改控制器中执行 csv 的任何内容下载,这以前有效...现在是什么导致 ResponseEntity 下载失败?
  • 也许ResponseEntity&lt;byte[]&gt; 是自动base-64 编码的?
  • @berimbolo 啊,答案大概是ByteArrayHttpMessageConverter;如果您只删除 @ResponseBody 会发生什么?
  • 知道为什么这会刚刚开始发生以及我需要做些什么来解决这个问题吗?
  • @berimbolo 也许这是默认弹簧行为的变化,或者默认包含哪些消息转换器?当您从原始方法中删除 @ResponseBody 时会发生什么?
【解决方案2】:

接受的解决方案在 Spring Boot 2.x 中对 Base64 图像不起作用。以下是我返回 Base64 图像的方式:

@GetMapping(value = "/pixels/{id}", produces = MediaType.IMAGE_PNG_VALUE)
@ResponseBody
public byte[] pixelTracking(@PathVariable String id) {

    // TODO: do whatever you want here

    // return png image
    String base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=";
    return Base64.getDecoder().decode(base64);
}

【讨论】:

    【解决方案3】:

    好的,所以我现在已经解决了这个问题,感谢 beerbajay,他向我展示了通过流式传输直接下载到响应是可以的,而且我应该查看 ByteArrayHttpMessageConverter。

    原来我在spring config中犯了一个错误,我在阅读spring文档后意识到这一点,告诉我ByteArrayHttpMessageConverter是在使用时自动注册的。

    mvc 注释驱动标签已从配置中删除,因为我认为这是在做同样的事情(我认为它只需要在 spring 上下文中声明一次):

       <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
        </mvc:message-converters>
    </mvc:annotation-driven>
    

    看来仅此一项不足以在应用程序中设置注释驱动,我已将标签添加回 context.xml 文件。

    我不明白为什么这需要在两个地方,因为所有的 xml 配置都是通过相同的 xml 文件加载的,我认为是相同的 spring 上下文:

    <import resource="config/properties.xml" />
    
    <import resource="config/security.xml" />
    
    <import resource="config/context.xml" />
    
    <import resource="config/content-negotiation.xml" />
    
    <import resource="config/rest-client.xml" />
    

    【讨论】:

    • 我已经完全删除了 content-negotiation.xml 文件,该文件无缘无故地设置了杰克逊内容协商,因为它已经通过 context.xml 中的 annotation-config 调用完成了。因此,当我删除注释配置时,看起来我只是倒退了,除了应用程序可以处理的图块内容之外,唯一的内容类型是 json。
    【解决方案4】:

    我的解决方案是:

    后端是spring boot,前端是reactjs

    错误是后端和前端的语法不同:java 使用 ("_","-") 和 web(reactjs,...) 使用 ("/","+") |
    例如:“PGjQOA66-_ne-”转换为“PGjQOA66+//ne/+”

    您可以在此链接中测试 base64:https://onlinepngtools.com/convert-base64-to-png

    后端代码:

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            File file = new File(path);/// path : is external directory and local directory in server
            FileInputStream imageInFile = new FileInputStream(path);
            byte imageData[] = new byte[(int) file.length()];
            imageInFile.read(imageData);
            String base64 =  encodeImage(imageData);
            /// 1. Not show if size up  300KB !!! :|
            /// 2. base64.replaceAll("_","/").replaceAll("-","+") 
            byte[] bytes = Base64.decodeBase64(base64.replaceAll("_","/").replaceAll("-","+"));
            BufferedImage bi = ImageIO.read(new ByteArrayInputStream(bytes));
            ImageIO.write(bi, "png", baos);
    
    
            HttpHeaders headers = new HttpHeaders();
            headers.setLastModified(Calendar.getInstance().getTime().getTime());
            headers.setCacheControl("no-cache");
            headers.setContentType(MediaType.IMAGE_PNG);
            headers.setContentLength(baos.toByteArray().length);
            return new ResponseEntity<byte[]>(baos.toByteArray(), headers, HttpStatus.OK);
    

    和其他后端解决方案:

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            File file = new File(path);/// path : is external directory file and local directory file in server, or path get of database.
            FileInputStream imageInFile = new FileInputStream(path);
            byte imageData[] = new byte[(int) file.length()];
            imageInFile.read(imageData);
            /// 1. Not show if size up  300KB !!! :| i dont now!
            /// 2. base64.replaceAll("_","/").replaceAll("-","+") 
            String base64 =  encodeImage(imageData).replaceAll("_","/").replaceAll("-","+");
            return base64;
    

    reactjs 代码是:

        const [imageData, setImageData] = React.useState({});
            
        setImageData(...request to backend);
        <img  src={`data:image/png;base64,${imageData}`} alt="This Is alert" />
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-18
      • 1970-01-01
      • 2014-02-15
      • 2018-01-08
      • 1970-01-01
      • 2018-01-29
      • 1970-01-01
      • 2014-09-03
      相关资源
      最近更新 更多