Spring 返回图像作为 ResponseEntity<byte[]> - 图像损坏

Spring returning image as ResponseEntity<byte[]> - image corrupt

我正在开发 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" />

我没有更改这个 class 并且我被告知它确实有效,我能够从两个字节数组创建图像并且它们没问题并且看起来相同并且我能够呈现签名字符串可以像这样进行测试:

  <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 {

我已经在 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 而不是实际的文件内容类型。

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

事实证明我在 spring 配置中犯了一个错误,我在阅读 spring 文档后意识到了这一点,该文档告诉我在使用 .

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" />

接受的解决方案在 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);
}

我的解决方案是:

后端是 spring boot,前端是 reactjs

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

你可以在这个 link 中测试 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" />