如何将Flux<DataBuffer>转换为Spring控制器中的StreamingResponseBody

huangapple go评论77阅读模式
英文:

How to convert a Flux<DataBuffer> into a StreamingResponseBody in a Spring Controller

问题

我在我的Spring Controller中有一个DataBuffer的Flux,并希望将其附加到StreamingResponseBody作为流。我已经阅读了许多类似方法的答案,但没有完全符合这个情况。我不想将整个文件加载到内存中,我想要流式传输。

@GetMapping(value="/attachment")
public ResponseEntity<StreamingResponseBody> get(@PathVariable long attachmentId) {

    Flux<DataBuffer> dataBuffer = this.myService.getFile(attachmentId);

    StreamingResponseBody stream = out -> {
       // 在这里要做什么?
    }

    return ResponseEntity.ok()
        .contentType(MediaType.APPLICATION_OCTET_STREAM)
        .body(stream);
}

EDIT: myService.getFile()

public Flux<DataBuffer> getFile(long attachmentId) {

    return this.webClient.get()
        .uri("https://{host}/attachments/{attachmentId}", host, attachmentId)
        .attributes(clientRegistrationId("attachmentClient"))
        .accept(MediaType.ALL)
        .exchangeToFlux(clientResponse -> clientResponse.bodyToFlux(DataBuffer.class));
}

希望这有所帮助。

英文:

I have a Flux of DataBuffer in my Spring Controller and I want to attach it to the StreamingResponseBody as a stream. I have read many answers for similar approaches, but nothing quite matches this. I do not want to load the entire file in memory. I want it streamed.

@GetMapping(value=&quot;/attachment&quot;)
public ResponseEntity&lt;StreamingResponseBody&gt; get(@PathVariable long attachmentId) {

    Flux&lt;DataBuffer&gt; dataBuffer = this.myService.getFile(attachmentId);

    StreamingResponseBody stream = out -&gt; {
       // what do do here?
    }

    return ResponseEntity.ok()
        .contentType(MediaType.APPLICATION_OCTET_STREAM)
        .body(stream);
    }
}

EDIT: myService.getFile()

public Flux&lt;DataBuffer&gt; gteFile(long attachmentId) {

    return this.webClient.get()
        .uri(&quot;https://{host}/attachments/{attachmentId}&quot;, host, attachmentId)
        .attributes(clientRegistrationId(&quot;attachmentClient&quot;))
        .accept(MediaType.ALL)
        .exchangeToFlux(clientResponse -&gt; clientResponse.bodyToFlux(DataBuffer.class));
}

答案1

得分: 1

如果您使用WebFlux,就不需要返回StreamingResponseBody,可以直接返回Flux&lt;DataBuffer&gt;,这样它就会进行流式传输并且是非阻塞的。

如果您想要添加一些标头/自定义您的响应,那么可以返回Mono&lt;ResponseEntity&lt;Flux&lt;DataBuffer&gt;&gt;&gt;

@GetMapping("/attachment/{attachmentId}")
public Mono<ResponseEntity<Flux<DataBuffer>>> get(@PathVariable long attachmentId) {

    Flux<DataBuffer> dataBuffers = this.myService.getFile(attachmentId);
    
    ContentDisposition contentDisposition = ContentDisposition.inline()
            .filename("example.txt")
            .build();

    HttpHeaders headers = new HttpHeaders();
    headers.setContentDisposition(contentDisposition);

    return Mono.just(ResponseEntity.ok()
            .headers(headers)
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(dataBuffers));
}

如果您想要使用StreamingResponseBody(这意味着您正在使用阻塞的Spring MVC在Web层),那么您可以执行以下操作:

StreamingResponseBody stream = outputStream ->
        Mono.create(sink -> DataBufferUtils.write(dataBuffers, outputStream)
                .subscribe(DataBufferUtils::release, sink::error, sink::success)
        ).block();

然后从您的控制器返回它。

英文:

If you're using WebFlux there's no need to return StreamingResponseBody, you can just return Flux&lt;DataBuffer&gt; directly, so it will be streaming and also non-blocking.

If you want to add some headers/customize your response, the you can return Mono&lt;ResponseEntity&lt;Flux&lt;DataBuffer&gt;&gt;&gt; :

<!-- language: java -->
@GetMapping("/attachment/{attachmentId}")
public Mono<ResponseEntity<Flux<DataBuffer>>> get(@PathVariable long attachmentId) {

    Flux&lt;DataBuffer&gt; dataBuffers = this.myService.getFile(attachmentId);
    
    ContentDisposition contentDisposition = ContentDisposition.inline()
            .filename(&quot;example.txt&quot;)
            .build();

    HttpHeaders headers = new HttpHeaders();
    headers.setContentDisposition(contentDisposition);

    return Mono.just(ResponseEntity.ok()
            .headers(headers)
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .body(dataBuffers));
}

If you want to use StreamingResponseBody (which means you're using blocking Spring MVC on web-layer), then you can do the following:

<!-- language: java -->

StreamingResponseBody stream = outputStream -&gt;
        Mono.create(sink -&gt; DataBufferUtils.write(dataBuffers, outputStream)
                .subscribe(DataBufferUtils::release, sink::error, sink::success)
        ).block();

and then return it from your controller

huangapple
  • 本文由 发表于 2023年7月14日 03:48:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76682781.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定