如何将MultipartFile转换为Mono<FilePart>?非响应式API场景
无临时文件实现MultipartFile到FilePart的流式转换
问题场景
现有响应式API定义如下,接收Mono<FilePart>作为参数:
Mono<FileType> processAndReturnType(Mono<FilePart> partFlux)
需要从非响应式API getType(MultipartFile partFile) 中调用该响应式接口,核心需求是将MultipartFile转换为Mono<FilePart>。原实现通过临时文件中转触发了安全告警,需要改为直接流式处理的方案。
优化后的实现方案
下面是无需临时文件的FilePart包装类实现,完全基于输入流流式处理:
package com.maersk.clm.naas.tenderfile.convertor; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.codec.multipart.FilePart; import org.springframework.web.multipart.MultipartFile; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.lang.NonNull; import reactor.core.scheduler.Schedulers; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Path; import java.util.Objects; /** * 基于MultipartFile实现的FilePart包装类,无临时文件依赖 */ public class MultipartFilePart implements FilePart { private final MultipartFile file; public MultipartFilePart(MultipartFile file) { this.file = file; } @Override @NonNull public Flux<DataBuffer> content() { // 直接从MultipartFile的输入流生成DataBuffer流,无需临时文件 return DataBufferUtils.readInputStream( () -> { try { return file.getInputStream(); } catch (IOException e) { throw new UncheckedIOException(e); } }, new DefaultDataBufferFactory(), 8192 // 缓冲区大小,可根据业务调整,8192为Spring默认值 ).onErrorMap(UncheckedIOException.class, IOException::new); } @Override @NonNull public String filename() { return Objects.requireNonNull(file.getOriginalFilename()); } @Override @NonNull public String name() { return file.getName(); } @Override @NonNull public Mono<Void> transferTo(@NonNull Path dest) { // 直接流式写入目标路径,无需清理临时文件 return DataBufferUtils.write(content(), dest) .then() .publishOn(Schedulers.boundedElastic()); } @Override @NonNull public HttpHeaders headers() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.parseMediaType(Objects.requireNonNull(file.getContentType()))); return headers; } }
关键优化点说明
- 流式内容生成:核心修改
content()方法,使用DataBufferUtils.readInputStream直接从MultipartFile的输入流创建Flux<DataBuffer>,彻底避免临时文件的创建,解决安全告警问题。 - 异常处理:将输入流获取时的
IOException包装为UncheckedIOException(因为Supplier不允许抛出受检异常),再通过onErrorMap转换回IOException,保证响应式流的错误信号符合预期。 - 简化transferTo逻辑:移除原实现中临时文件的清理逻辑,因为现在没有临时文件需要处理,实现更简洁。
- 缓冲区大小:使用Spring默认的8192字节缓冲区,可根据大文件场景调整为更大的值(如16384)以提升性能。
调用示例
在非响应式API中,按如下方式调用响应式接口:
public FileType getType(MultipartFile partFile) { // 将MultipartFile包装为FilePart并转为Mono Mono<FilePart> filePartMono = Mono.just(new MultipartFilePart(partFile)); // 阻塞获取响应式API结果(非响应式环境下合理操作) return processAndReturnType(filePartMono).block(); }
注意:如果业务允许,建议逐步将上层API改造为响应式,避免使用
block()带来的线程阻塞问题。
内容的提问来源于stack exchange,提问作者Vivek
相关产品推荐
相关产品推荐

