如何在 Spring Webflux 中获取 FilePart 的大小

How to get the size of FilePart in Spring Webflux

无法弄清楚如何在 REST 端点中使用 FilePart 获取文件的实际大小:

@RestController
public class SomeController {

    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Mono<Long> fileSize(Mono<FilePart> filePart) {
        
        //How to get size of FilePart?

        // I'm not plan to create a File saving content of FilePart. 
        // Maybe it's possible somehow calculate size of all bytes.

        return Mono.empty();
    }
}

更新 以下是我进行文件大小和图像分辨率验证的方式。

控制器:

@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseStatus(HttpStatus.CREATED)
public Mono<Void> upload(Mono<FilePart> file) {
    return file.flatMap(filepart -> fileService.upload(filepart));
}

服务层的验证方法:

private Mono<byte[]> validateFileSize(FilePart filePart) {
    var maxSize = 1024;
    return FilePartUtils.getByteArray(filePart)
            .filter(bytes -> bytes.length <= maxSize)
            .switchIfEmpty(Mono.error(() -> new InvalidStateException(MAX_FILE_SIZE_REACHED, Map.of("size", maxSize))));
}

private Mono<Resolution> validateResolution(byte[] fileBytes) {
    // Use image.getWidth and image.getHeight to check resolution against you expectations
    return convertToImage(fileBytes).map(image -> validateResolution(image)); 
}

public Mono<BufferedImage> convertToImage(byte[] byteArray) {
    try {
        // but ImageIO.read is blocking method
        return Mono.just(ImageIO.read(new ByteArrayInputStream(byteArray)));
    } catch (IOException e) {
        return Mono.error(new InvalidStateException(UPLOAD_FILE_ABORTED, Map.of()));
    }
}

我也不知道如何在不先将文件保存到文件或将其缩小为 InputStream 或类似的东西的情况下获取文件大小。

我最后做的是向请求添加自定义 header。然后,您可以在控制器中读取 header:

@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Mono<Long> fileSize(Mono<FilePart> filePart, @RequestHeader("File-Size") Long fileSize) {

如果您将新参数的类型更改为 Map<String, String>MultiValueMap<String, String>HttpHeaders,您将获得所有 header,而不仅仅是您的自定义参数.

如果你不能改变客户端的请求headers,并且你不需要文件大小绝对精确,你可以使用默认的header Content-Length , 已经发送了,但是会少几个字节。

当您将文件作为多部分表单数据上传时,会自动创建一个名为“Content-Length”的请求header,该请求是在发送请求时计算的.

但是请注意,多部分 content-length 略大于文件大小 由于创建了一些元数据,例如文件名、边界和所有内容。而且 没有办法 您可以估计此元数据的长度。解释是 here.

这样就可以得到尺寸(略微增大):

@RestController
public class SomeController {

    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Mono<Long> fileSize(Mono<FilePart> filePart, @RequestHeader("Content-Length") long contentLength) {
        System.out.println("Content length is:" + contentLength + "bytes");
        return Mono.empty();
    }

}

您可以使用 filePart.content() 访问文件部分中的 Flux<DataBuffer>。使用这个。

filePart.content() // for one file, is a Flux with one element
    .map(dataBuffer -> {
         byte[] bytes = new byte[dataBuffer.readableByteCount()]; // <---- here you count the bytes you have. 
         dataBuffer.read(bytes);
         DataBufferUtils.release(dataBuffer);
         String content = new String(bytes, StandardCharsets.UTF_8);
         if (log.isTraceEnabled()) log.trace("File name: {}, content: {}", filePart.filename(),
             content.length() > CONTENT_LENGTH_LIMIT ? content.substring(0, CONTENT_LENGTH_LIMIT) + "..." : content);
         return content;
    })