【问题标题】:SprintBoot returning a PNG from a Controller's RequestMappingSpring Boot 从控制器的 RequestMapping 返回 PNG
【发布时间】:2016-12-31 01:16:51
【问题描述】:

我一直在互联网上搜索资源,我觉得我几乎可以找到答案,但我似乎无法将 BufferedImage 返回到浏览器窗口。

项目生成一个迷宫,然后可以创建一个BufferedImage

这是来自我的控制器的代码。

    @RequestMapping(method = RequestMethod.GET, path = "/image", params = {"rows", "columns"})
    public ResponseEntity<byte[]> image(@RequestParam(name = "rows") int rows, @RequestParam(name = "columns") int columns) throws IOException, InterruptedException {
        try {
            BasicCartesianGrid requestedMaze = new BasicCartesianGrid(rows, columns);
            requestedMaze.forEach(CellAlgorithms.BINARY_TREE);
            BufferedImage bufferedImage = requestedMaze.toDisplayImage();
            {   // Dumping to file for debugging <- this works as expected
                File outputFile = new File("save.png");
                ImageIO.write(bufferedImage, "png", outputFile);
            }
            ByteArrayOutputStream pngByteStream = new ByteArrayOutputStream();
            ImageIO.write(bufferedImage, "png", pngByteStream);
            byte[] pngBytes = pngByteStream.toByteArray();
            final HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.IMAGE_PNG);
            headers.setContentLength(pngBytes.length);
            headers.setCacheControl(CacheControl.noCache().getHeaderValue());

            return new ResponseEntity<>(pngBytes, headers, HttpStatus.OK);
        } catch (Exception e) {
            // This hasn't occurred yet, but is for just in case
            Thread.sleep(1000);
            System.err.println(e.getLocalizedMessage());

            final HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.TEXT_PLAIN);
            return new ResponseEntity<>(e.getLocalizedMessage().getBytes("ASCII"), headers, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

我已确定正确生成了 PNG,因为该文件存在并且可以在我的硬盘驱动器上查看。我的浏览器返回了损坏的图像。通过我的终端,我可以获得更多信息。

curl "http://localhost:8080/maze/image?rows=10&columns=10"

转储以下内容(引号是响应的一部分,而省略号表示的数据因请求而异,因为每个迷宫都是随机生成且唯一的):

"iVBORw0KGgoAAAANSUhEUgAAA......"

我搜索了这个字符串前缀,找到了this page。这表明该字符串应该用作 data-uri,如下所示:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA…" >

我不知道从这里去哪里。看起来我的图像生成正确,但我的响应中必须缺少一个标题,以告诉浏览器/spring这些字节应该被解释为图像而不是字符串。

更新: 根据我和答案部分的Shawn Clark 之间的对话,这是我目前所拥有的。

@SpringBootApplication
@Log4j
public class SpringMazesApplication {

    @Bean
    public HttpMessageConverter<BufferedImage> bufferedImageHttpMessageConverter() {
        log.debug("Registering BufferedImage converter");
        return new BufferedImageHttpMessageConverter();
    }

    public static void main(String[] args) throws IOException {
        SpringApplication.run(SpringMazesApplication.class, args);
    }
}

以及实际控制人:

@Controller
@RequestMapping(path = "/maze/basic", method = RequestMethod.GET)
@Log4j
public class BasicMazeController {

    @RequestMapping(params = {"format", "format=text"}, produces = MediaType.TEXT_PLAIN_VALUE)
    @ResponseBody
    public String simpleMazeText(@RequestParam(name = "rows", defaultValue = "10", required = false) int rows,
                                 @RequestParam(name = "columns", defaultValue = "10", required = false) int columns) throws IOException {
        BasicCartesianGrid requestedMaze = new BasicCartesianGrid(rows, columns);
        requestedMaze.forEach(CellAlgorithms.BINARY_TREE);
        return requestedMaze.toDisplayString();
    }

    @RequestMapping(params = {"format=image"}, produces = MediaType.IMAGE_PNG_VALUE)
    @ResponseBody
    public BufferedImage simpleMazeImage(@RequestParam(name = "rows", defaultValue = "10", required = false) int rows,
                                         @RequestParam(name = "columns", defaultValue = "10", required = false) int columns) throws IOException {
        log.debug("Starting image generation");
        BasicCartesianGrid requestedMaze = new BasicCartesianGrid(rows, columns);
        requestedMaze.forEach(CellAlgorithms.BINARY_TREE);
        BufferedImage bufferedImage = requestedMaze.toDisplayImage();
        {   // Dumping to file for debugging <- this works as expected
            log.debug("Dumping image to hd");
            File outputFile = new File("save.png");
            ImageIO.write(bufferedImage, "png", outputFile);
        }
        log.debug("Returning from image generation");
        return bufferedImage;
    }

    @RequestMapping
    @ResponseBody
    public ResponseEntity<String> simpleMazeInvalid(@RequestParam(name = "rows", defaultValue = "10", required = false) int rows,
                                                    @RequestParam(name = "columns", defaultValue = "10", required = false) int columns,
                                                    @RequestParam(name = "format") String format) throws IOException {
        final HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.TEXT_PLAIN);
        return new ResponseEntity<>("Invalid format: " + format, headers, HttpStatus.BAD_REQUEST);
    }
}

从我的终端我可以curl -D - "url" 并且我可以通过日志记录/调试和我的终端的输出看到,转换器在应用程序的请求中正确注册,并且我得到了你所期望的响应除了返回406 Not Acceptable 的实际图像uri 之外的所有内容。如果我从图像方法中删除@ResponseBody,它只会返回一个500。我可以验证图像是否正确生成,因为它正按预期写入磁盘。

【问题讨论】:

  • 不要将其作为ResponseEntity 返回,只需返回BufferedImage。对于异常处理部分,请使用带有 @ExceptionHandler 注释的方法。

标签: rest http spring-boot png


【解决方案1】:

查看@RequestMapping 上的produces 属性。您可能希望将其设置为 image/png

这是一个完整的例子:

@RestController
public class ProduceImage {
    @GetMapping(path = "/image", produces = "image/png")
    public BufferedImage image() throws Exception {
        BufferedImage bufferedImage = ImageIO.read(new File("E:\\Downloads\\skin_201305121633211421.png"));
        return bufferedImage;
    }
}

我的 BufferedImage 是来自我的计算机的东西,但它可以很容易地成为您从 requestedMaze.toDisplayImage() 获得的 BufferedImage,而无需执行所有其他工作。要完成这项工作,您需要在上下文中包含 BufferedImageHttpMessageConverter

@Bean
public HttpMessageConverter<BufferedImage> bufferedImageHttpMessageConverter() {
    return new BufferedImageHttpMessageConverter();
}

【讨论】:

  • 我已经尝试将其作为我的众多排列之一,但没有任何效果。不过还是谢谢。
  • 如果您使用 Postman 之类的工具执行请求。响应头是什么?
  • HTTP/1.1 200 Cache-Control: no-cache Content-Type: image/png Content-Length: 2587 Date: Wed, 24 Aug 2016 06:02:17 GMT
  • 用一个例子更新了我的答案。
  • 这似乎应该几乎可以工作,我会从我的最后更新。
猜你喜欢
  • 2018-07-14
  • 1970-01-01
  • 1970-01-01
  • 2020-01-26
  • 2022-11-04
  • 2012-09-20
  • 1970-01-01
  • 1970-01-01
  • 2020-07-01
相关资源
最近更新 更多