【问题标题】:Spring Webflux Rest Controller for image response correct return type用于图像响应正确返回类型的 Spring Webflux Rest Controller
【发布时间】:2023-02-08 17:19:34
【问题描述】:

我正在创建一个返回图像的 Spring WebFlux 控制器。基于类似的问题,我在下面尝试了这个选项。

@RestController
@RequestMapping(value = "/image/*")
public class ImageController {
@RequestMapping(method = RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)
    protected void doGet(ServerHttpRequest request, ServerHttpResponse response) {
       try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(img, "jpg", baos);
            DataBuffer imageData = response.bufferFactory().wrap(barr);
            //returns empty content in response
            //response.writeWith(Flux.just(imageData));
            //returns valid image
            response.writeWith(Flux.just(imageData)).block();
            } catch (Exception e) {
                e.printStackTrace();
            } 
        }

图像有效,我还可以看到 DataBuffer 有字节。但是,它不会将响应发送回客户端。

对该服务的响应是成功的(状态 200),但负载为空(0 字节)。

任何人都可以在这里识别错误吗?

[更新]

我通过添加对块的调用让控制器返回图像:

response.writeWith(Flux.just(imageData)).block();

我还看到它通过调用 subscribe() 起作用:

response.writeWith(Flux.just(imageData)).subscribe();

这种控制器的正确方法是什么?这是否等同于 Stream 上的 flush() 调用? WebFlux 框架不应该在控制器方法完成后阻塞或刷新响应对象吗?

类似问题:

How to write messages to http body in Spring webflux WebExceptionHandlder

https://stackoverflow.com/a/58351771/6352160

【问题讨论】:

  • 永远不要阻塞,因为它会严重损害性能。仅当您是最终消费者时才订阅。不要返回 void,反应函数不应该返回 void,而是返回 Mono<Void>

标签: java spring spring-webflux reactive


【解决方案1】:

这是我如何让它工作的。这是一个简单的问题,但我遵循的是用于传统 Spring MVC 的相同方法(将图像作为字节流写入 servlet 响应)。

 @GetMapping(produces = MediaType.IMAGE_PNG_VALUE)
 public Mono<DataBuffer> doGet(ServerHttpRequest request) {
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(img, "png", baos);
        baos.close();
        byte[] barr = baos.toByteArray();
        DataBuffer imageData =  
           DefaultDataBufferFactory.sharedInstance.wrap(barr);
        return Mono.just(imageData);
    } catch (Exception e) {
        LOG.error("An unknown IO error occurred while writing pixel", e);
    } 
    return Mono.empty();
}

【讨论】:

    【解决方案2】:

    WebFlux 提供异步处理,可以使用异步处理,而文件/字节数据可以作为流提供。

    如果将 Netty 与 WebFlux 一起使用,那么它会在内部提供 DataBuffer 工厂,其中数据文件被分成块(具有给定的缓冲区大小),并且块在响应时流式传输。

        private val dataBufferFactory: DataBufferFactory = NettyDataBufferFactory(ByteBufAllocator.DEFAULT)
    
         //The producer define the media type based on image type is available in MediaType class else Octet-Stream can be used, accept need to pass in request header
        @GetMapping(produces = [IMAGE_JPEG_VALUE, IMAGE_PNG_VALUE, IMAGE_GIF_VALUE, APPLICATION_OCTET_STREAM_VALUE])
        fun imageStream(): Mono<ResponseEntity<Flux<DataBuffer>>> {
                val filename = "test.png"
                val bufferSize = 8192 //Define buffer size, number of Flux's elements = total file size / bufferSize
                val headers = HttpHeaders()
                headers.setContentDispositionFormData(FILENAME, filename)
                val resource = ClassPathResource("$filename") //There are various implementations of Resource interface which can be used based on need
                Flux<DataBuffer> dataFlux = DataBufferUtils.read(resource, dataBufferFactory, bufferSize) //Important utility which converts the Resource object to Flux<DataBuffer>
                ResponseEntity
                    .ok()
                    .headers(headers)
                    .body(idataFlux)
                .doOnSuccess { logger.debug("Stream request success") }
                .doOnError { logger.debug("Stream request error") }
        }
    

    这使得性能更快,因为内存将根据定义的缓冲区大小而不是一次读取完整的文件来使用。

    【讨论】:

      猜你喜欢
      • 2021-03-29
      • 1970-01-01
      • 2020-02-19
      • 2023-02-17
      • 2022-07-31
      • 1970-01-01
      • 2015-12-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多