我正在创建一个返回图像的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();
这种控制器的正确方法是什么?这相当于流上的flush((调用吗?WebFlux框架不应该在控制器方法完成后阻塞或刷新响应对象吗?
类似问题:
如何在Spring webflux WebExceptionHandlder 中向http主体写入消息
https://stackoverflow.com/a/58351771/6352160
以下是我如何做到这一点的。这是一个简单的问题,但我采用了与传统SpringMVC相同的方法(将图像作为字节流写入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();
}
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") }
}
这使得性能更快,因为内存将根据定义的缓冲区大小来利用,而不是一次读取完整的文件。
除了DataBuffer
返回,您还可以从控制器方法返回spring-core的Resource
接口的任何实现。例如:
ClassPathResource
FileSystemResource
InputStreamResource
Spring还有许多其他可用于Resource
接口的实现。
返回类型类似于:Mono<ResponseEntity<InputStreamResource>>
在Resource
返回类型中,缓冲区大小由框架管理,但资源的数据将像DataBuffer实现中一样进行流式传输。