SprintBoot 从控制器的 RequestMapping 返回 PNG

Pau*_*ker 2 rest png http spring-boot

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

\n\n

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

\n\n

这是我的控制器的代码。

\n\n
    @RequestMapping(method = RequestMethod.GET, path = "/image", params = {"rows", "columns"})\n    public ResponseEntity<byte[]> image(@RequestParam(name = "rows") int rows, @RequestParam(name = "columns") int columns) throws IOException, InterruptedException {\n        try {\n            BasicCartesianGrid requestedMaze = new BasicCartesianGrid(rows, columns);\n            requestedMaze.forEach(CellAlgorithms.BINARY_TREE);\n            BufferedImage bufferedImage = requestedMaze.toDisplayImage();\n            {   // Dumping to file for debugging <- this works as expected\n                File outputFile = new File("save.png");\n                ImageIO.write(bufferedImage, "png", outputFile);\n            }\n            ByteArrayOutputStream pngByteStream = new ByteArrayOutputStream();\n            ImageIO.write(bufferedImage, "png", pngByteStream);\n            byte[] pngBytes = pngByteStream.toByteArray();\n            final HttpHeaders headers = new HttpHeaders();\n            headers.setContentType(MediaType.IMAGE_PNG);\n            headers.setContentLength(pngBytes.length);\n            headers.setCacheControl(CacheControl.noCache().getHeaderValue());\n\n            return new ResponseEntity<>(pngBytes, headers, HttpStatus.OK);\n        } catch (Exception e) {\n            // This hasn\'t occurred yet, but is for just in case\n            Thread.sleep(1000);\n            System.err.println(e.getLocalizedMessage());\n\n            final HttpHeaders headers = new HttpHeaders();\n            headers.setContentType(MediaType.TEXT_PLAIN);\n            return new ResponseEntity<>(e.getLocalizedMessage().getBytes("ASCII"), headers, HttpStatus.INTERNAL_SERVER_ERROR);\n        }\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
curl "http://localhost:8080/maze/image?rows=10&columns=10"\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
"iVBORw0KGgoAAAANSUhEUgAAA......"\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA\xe2\x80\xa6" >\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n
@SpringBootApplication\n@Log4j\npublic class SpringMazesApplication {\n\n    @Bean\n    public HttpMessageConverter<BufferedImage> bufferedImageHttpMessageConverter() {\n        log.debug("Registering BufferedImage converter");\n        return new BufferedImageHttpMessageConverter();\n    }\n\n    public static void main(String[] args) throws IOException {\n        SpringApplication.run(SpringMazesApplication.class, args);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

及实际控制人:

\n\n
@Controller\n@RequestMapping(path = "/maze/basic", method = RequestMethod.GET)\n@Log4j\npublic class BasicMazeController {\n\n    @RequestMapping(params = {"format", "format=text"}, produces = MediaType.TEXT_PLAIN_VALUE)\n    @ResponseBody\n    public String simpleMazeText(@RequestParam(name = "rows", defaultValue = "10", required = false) int rows,\n                                 @RequestParam(name = "columns", defaultValue = "10", required = false) int columns) throws IOException {\n        BasicCartesianGrid requestedMaze = new BasicCartesianGrid(rows, columns);\n        requestedMaze.forEach(CellAlgorithms.BINARY_TREE);\n        return requestedMaze.toDisplayString();\n    }\n\n    @RequestMapping(params = {"format=image"}, produces = MediaType.IMAGE_PNG_VALUE)\n    @ResponseBody\n    public BufferedImage simpleMazeImage(@RequestParam(name = "rows", defaultValue = "10", required = false) int rows,\n                                         @RequestParam(name = "columns", defaultValue = "10", required = false) int columns) throws IOException {\n        log.debug("Starting image generation");\n        BasicCartesianGrid requestedMaze = new BasicCartesianGrid(rows, columns);\n        requestedMaze.forEach(CellAlgorithms.BINARY_TREE);\n        BufferedImage bufferedImage = requestedMaze.toDisplayImage();\n        {   // Dumping to file for debugging <- this works as expected\n            log.debug("Dumping image to hd");\n            File outputFile = new File("save.png");\n            ImageIO.write(bufferedImage, "png", outputFile);\n        }\n        log.debug("Returning from image generation");\n        return bufferedImage;\n    }\n\n    @RequestMapping\n    @ResponseBody\n    public ResponseEntity<String> simpleMazeInvalid(@RequestParam(name = "rows", defaultValue = "10", required = false) int rows,\n                                                    @RequestParam(name = "columns", defaultValue = "10", required = false) int columns,\n                                                    @RequestParam(name = "format") String format) throws IOException {\n        final HttpHeaders headers = new HttpHeaders();\n        headers.setContentType(MediaType.TEXT_PLAIN);\n        return new ResponseEntity<>("Invalid format: " + format, headers, HttpStatus.BAD_REQUEST);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

卷曲休息点的终端输出

\n

Sha*_*ark 6

检查一下produces上的属性@RequestMapping。您可能希望将其设置为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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我的 BufferedImage 是来自我的计算机的东西,但它可以像您从计算机中获得的 BufferedImage 一样轻松,而requestedMaze.toDisplayImage()无需执行所有其他工作。为了使这项工作有效,您需要将其包含BufferedImageHttpMessageConverter在您的上下文中。

@Bean
public HttpMessageConverter<BufferedImage> bufferedImageHttpMessageConverter() {
    return new BufferedImageHttpMessageConverter();
}
Run Code Online (Sandbox Code Playgroud)