簡體   English   中英

UnsupportedMediaTypeException with Spring 和 Reactor

[英]UnsupportedMediaTypeException with Spring and Reactor

我嘗試使用 class ResourceRegion 使用 Spring 和反應器 stream 文件。

我有這個方法:

public Mono<ServerResponse> getPartialVideoById(ServerRequest request) {

        Long idVideo = Long.valueOf(request.pathVariable("idVideo"));

        HttpHeaders requestHeaders = request.headers().asHttpHeaders();

        Optional<UrlResource> video = this.videoService.getUrlResourceById(idVideo);

        Optional<ResourceRegion> resourceRegion = video.map(v -> {
            try {
                return this.videoService.getRegion(v, requestHeaders);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        });
        return ServerResponse.status(HttpStatus.PARTIAL_CONTENT)
                .contentType(MediaTypeFactory.getMediaType(video.get()).orElse(MediaType.APPLICATION_OCTET_STREAM))
                .contentLength(resourceRegion.get().getCount())
                .headers(headers -> headers.setCacheControl(CacheControl.noCache()))
                .body(Mono.just(resourceRegion.get()), ResourceRegion.class).flatMap(response -> {
                    if (response.headers().getContentLength() == 0) {
                        return Mono.error(new ResourceNotFound());
                    }
                    return Mono.just(response);
                });
    }

我的終點是:

@Bean
        RouterFunction<ServerResponse> videoEndPoint(VideoRouteHandler videoRouteHandler) {

            return route(GET("/video/{idVideo}"), videoRouteHandler::getPartialVideoById)
                    .filter((request, next) -> next.handle(request)
                            .onErrorResume(ErrorHandler::handleError));    
       }

一切都是在 SpringBoot 的嵌入式 Netty 下啟動的,但是當我嘗試調用 api 時,我必須出現以下錯誤:

org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'video/x-msvideo' not supported for bodyType=org.springframework.core.io.support.ResourceRegion

我嘗試了不同類型的文件(avi、mkv、mp4、....還有 pdf....),但內容類型仍然存在問題。

有任何想法嗎?

您需要此 class 作為配置

import org.springframework.context.annotation.Configuration
import org.springframework.http.codec.ServerCodecConfigurer
import org.springframework.web.reactive.config.WebFluxConfigurer


@Configuration
class WebFluxConfig: WebFluxConfigurer {

    override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
        configurer.customCodecs().writer(ResourceRegionMessageWriter())
    }
}

還有作者...

import org.reactivestreams.Publisher
import org.springframework.core.ResolvableType
import org.springframework.core.codec.ResourceRegionEncoder
import org.springframework.core.io.Resource
import org.springframework.core.io.support.ResourceRegion
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.http.MediaTypeFactory
import org.springframework.http.ReactiveHttpOutputMessage
import org.springframework.http.ZeroCopyHttpOutputMessage
import org.springframework.http.codec.HttpMessageWriter
import org.springframework.http.server.reactive.ServerHttpRequest
import org.springframework.http.server.reactive.ServerHttpResponse
import reactor.core.publisher.Mono
import java.io.File
import java.lang.Long.min
import java.util.*


class ResourceRegionMessageWriter : HttpMessageWriter<ResourceRegion> {


    private val log = logger()


    private val resourceRegionEncoder = ResourceRegionEncoder()


    override fun getWritableMediaTypes(): MutableList<MediaType> {

        return MediaType.asMediaTypes(resourceRegionEncoder.encodableMimeTypes)
    }

    override fun canWrite(elementType: ResolvableType, mediaType: MediaType?): Boolean {

        return resourceRegionEncoder.canEncode(elementType, mediaType)
    }

    override fun write(inputStream: Publisher<out ResourceRegion>, elementType: ResolvableType, mediaType: MediaType?, message:     ReactiveHttpOutputMessage, hints: MutableMap<String, Any>): Mono<Void> {
        TODO("Not yet implemented")
    }

    override fun write(inputStream: Publisher<out ResourceRegion>,
                               actualType: ResolvableType,
                               elementType: ResolvableType,
                               mediaType: MediaType?,
                               request: ServerHttpRequest,
                               response: ServerHttpResponse,
                               hints: Map<String, Any>): Mono<Void> {


        val headers: HttpHeaders = response.headers
        headers[HttpHeaders.ACCEPT_RANGES] = "bytes"

        return Mono.from(inputStream).flatMap { resourceRegion: ResourceRegion ->

            val contentLength = headers.contentLength
            val requestHeaders: HttpHeaders = request.headers
            val range = if (requestHeaders.range.isNotEmpty()) requestHeaders.range[0] else null

            if (range != null) {

                val start = range.getRangeStart(contentLength)
                val end: Long = min(start + resourceRegion.count - 1, contentLength - 1)

                val contentRange = "bytes $start-$end/$contentLength"
                log.debug("contentRange: $contentRange")

                headers.add(HttpHeaders.CONTENT_RANGE, contentRange)
                headers.contentLength = end - start + 1
            }

            val resourceMediaType = getResourceMediaType(mediaType, resourceRegion.resource)

            zeroCopy(resourceRegion.resource, resourceRegion, response).orElseGet {

                val input = Mono.just(resourceRegion)
                val body = resourceRegionEncoder.encode(input, response.bufferFactory(),
                        ResolvableType.forClass(ResourceRegion::class.java), resourceMediaType, Collections.emptyMap())

                response.writeWith(body)
            }

        }.doOnError { obj: Throwable -> obj.printStackTrace() }

    }

    private fun getResourceMediaType(mediaType: MediaType?, resource: Resource): MediaType? {

        return if (Objects.nonNull(mediaType) && mediaType!!.isConcrete && mediaType !== MediaType.APPLICATION_OCTET_STREAM) {
            mediaType
        } else {
            MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM)
        }
    }

    private fun zeroCopy(resource: Resource, resourceRegion: ResourceRegion,
                         message: ReactiveHttpOutputMessage): Optional<Mono<Void>> {

        if (message is ZeroCopyHttpOutputMessage && resource.isFile) {

            try {

                val file: File = resource.file
                val position = resourceRegion.position
                val count = resourceRegion.count

                return Optional.of(message.writeWith(file, position, count))

            } catch (ignored: Exception) {}
        }

        return Optional.empty()
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM