![](/img/trans.png)
[英]How to handle UnsupportedMediaTypeException in Spring Boot?
[英]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.