Spring boot SpringWebFlux文件部分如何验证mime类型?

Spring boot SpringWebFlux文件部分如何验证mime类型?,spring-boot,spring-webflux,Spring Boot,Spring Webflux,考虑到下面的控制器映射,我想知道如何验证提供的文件是否是具有最大文件大小的图像 @PostMapping("/users/avatar") suspend fun uploadAvatar(@RequestPart("file") filePartMono: Mono<FilePart>) { filePartMono.map{ part -> part.content().map{ it.asInputStream().readAl

考虑到下面的控制器映射,我想知道如何验证提供的文件是否是具有最大文件大小的图像

@PostMapping("/users/avatar")
suspend fun uploadAvatar(@RequestPart("file") filePartMono: Mono<FilePart>) {
  filePartMono.map{ part -> part.content().map{ it.asInputStream().readAllBytes() }
    .map { it /* ??? */ }
  }
  // ...
}
@PostMapping(“/users/avatar”)
暂停乐趣上传化身(@RequestPart(“文件”)filePartMono:Mono){
filePartMono.map{part->part.content().map{it.asInputStream().readAllBytes()}
.map{it/*???*/}
}
// ...
}
在非反应性堆栈上,我是这样做的:

uploadAvatar(@RequestParam("file") MultipartFile file) {
    String contentType = file.getContentType().toLowerCase();
    List<String> valid = Arrays.asList("image/jpg", "image/jpeg", "image/png");
    if (file.isEmpty() || !valid.contains(contentType)) {
        throw new UploadException("Invalid image format or empty file", HttpStatus.UNPROCESSABLE_ENTITY);
    }
    if (file.getSize() > IMAGE_MAX_FILE_SIZE) {
        throw new UploadException("Image too big (max " + (IMAGE_MAX_FILE_SIZE / 1000) + " kB )", HttpStatus.UNPROCESSABLE_ENTITY);
    }
    try {
        // try to read file as image to ensure it is an image
        ImageIO.read(file.getInputStream());
    } catch (IOException e) {
        throw new UploadException("Invalid image", HttpStatus.UNPROCESSABLE_ENTITY);
    }

    try {
        byte[] bytes = file.getBytes();
        userService.storeAvatar(bytes);
    } catch (IOException e) {
        throw new UploadException("Could not store image", HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
uploadAvatar(@RequestParam(“文件”)多部分文件){
字符串contentType=file.getContentType().toLowerCase();
List valid=Arrays.asList(“image/jpg”、“image/jpeg”、“image/png”);
if(file.isEmpty()| |!valid.contains(contentType)){
抛出新的UploadException(“无效的图像格式或空文件”,HttpStatus.UNPROCESSABLE_ENTITY);
}
if(file.getSize()>IMAGE\u MAX\u file\u SIZE){
抛出新的UploadException(“图像太大(最大”+(图像最大文件大小/1000)+“kB)”,HttpStatus.UNPROCESSABLE_ENTITY);
}
试一试{
//尝试将文件作为图像读取,以确保它是图像
read(file.getInputStream());
}捕获(IOE异常){
抛出新的UploadException(“无效图像”,HttpStatus.UNPROCESSABLE_实体);
}
试一试{
byte[]bytes=file.getBytes();
userService.storeAvatar(字节);
}捕获(IOE异常){
抛出新的上载异常(“无法存储映像”,HttpStatus.INTERNAL\u SERVER\u错误);
}
}
但是FilePart不提供相同的API(例如contentType)


如何从
FilePart
中获取contentType和文件大小?

FilePart
具有您可以这样访问的标题

@PostMapping(value = ["/upload"], consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
suspend fun upload(@RequestPart(name = "file") file: Mono<FilePart>) {

    val filePart = file.awaitFirstOrNull() ?: throw Exception()

    println(filePart.headers().contentType)
    println(filePart.headers().contentLength)
}
@PostMapping(value = ["/upload"], consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
suspend fun upload(@RequestPart(name = "file") file: Mono<FilePart>) : Response {

    val filePart = file.awaitFirstOrNull() ?: throw Exception()
    val inputStream = filePart.content().awaitFirst().asInputStream()
    val detectedFileTye = getFilePartFileType(filePart.filename(), inputStream)
    ...
}

private suspend fun getFilePartFileType(fileName: String, inputStream: InputStream): String? = coroutineScope {

    val mimeTypeDeferred = async(Dispatchers.IO) {

        Tika().detect(inputStream, fileName)
    }

    mimeTypeDeferred.await()
}
然而,
Tika
detect正在阻止调用,所以您需要这样做

@PostMapping(value = ["/upload"], consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
suspend fun upload(@RequestPart(name = "file") file: Mono<FilePart>) {

    val filePart = file.awaitFirstOrNull() ?: throw Exception()

    println(filePart.headers().contentType)
    println(filePart.headers().contentLength)
}
@PostMapping(value = ["/upload"], consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
suspend fun upload(@RequestPart(name = "file") file: Mono<FilePart>) : Response {

    val filePart = file.awaitFirstOrNull() ?: throw Exception()
    val inputStream = filePart.content().awaitFirst().asInputStream()
    val detectedFileTye = getFilePartFileType(filePart.filename(), inputStream)
    ...
}

private suspend fun getFilePartFileType(fileName: String, inputStream: InputStream): String? = coroutineScope {

    val mimeTypeDeferred = async(Dispatchers.IO) {

        Tika().detect(inputStream, fileName)
    }

    mimeTypeDeferred.await()
}
@PostMapping(值=[“/upload”],使用=[MediaType.MULTIPART\u FORM\u DATA\u value])
暂停趣味上传(@RequestPart(name=“file”)文件:Mono):响应{
val filePart=file.waitingfirstornull()?:抛出异常()
val inputStream=filePart.content().awaitFirst().asInputStream()
val detectedFileTye=getFilePartFileType(filePart.filename(),inputStream)
...
}
private suspend fun getFilePartFileType(文件名:String,inputStream:inputStream):String?=共线镜{
val mimetypederferred=async(Dispatchers.IO){
Tika().detect(inputStream,文件名)
}
mimetypedereferred.await()
}

我唯一不明白的是private helper函数的返回类型
FileType?
。它指的是哪个包裹?
mimetypederferred
的类型为
Deferred
。很抱歉,它应该是
字符串
错误的复制/粘贴。我正在使用
文件类型
,但它是一个与此处无关的自定义对象。谢谢!我学到了很多。您能告诉我您是如何通过tearray获得实际的
数据的吗?根据我所读的,tika将重置inputstream,以便我们可以重用它,我现在将使用相同的模式
val bytes=coroutineScope{val bytesderferred=async(Dispatchers.IO){inputstream.readAllBytes()}bytesderferred.wait()}
是的,这是一种方法。就我而言,没有必要使用ByteArray。