Spring WebFlux如何直接获取Multipart FilePart的原始文件路径?
我们的Spring WebFlux应用通过端点接收包含文件的multipart数据,上传完成后需要将文件传递给同一服务器上的杀毒扫描器。目前使用FilePart#transferTo(dest)的方案会创建文件副本,而Spring内部存储的文件实际Path存在DefaultParts的私有字段中,没有公开方法可直接访问。我们不想用反射的方式获取该Path(示例代码如下),希望找到更可靠的替代方案:
return Mono.fromCallable(() -> { final Object content = FieldUtils.readField(filePart, "content", true); return (Path) FieldUtils.readField(content, "file", true); });
可行解决方案
1. 自定义文件存储策略(推荐)
通过自定义FileStorageStrategy,在文件上传阶段就跟踪并记录文件的本地Path,后续无需复制即可直接获取。这种方式不依赖Spring内部实现,稳定性和可维护性更高。
步骤1:实现可跟踪的文件存储策略
继承Spring默认的DefaultFileStorageStrategy,重写文件创建方法,将生成的文件Path与唯一标识关联存储:
import org.springframework.http.codec.multipart.DefaultFileStorageStrategy; import java.nio.file.Path; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; public class TrackableFileStorageStrategy extends DefaultFileStorageStrategy { private final Map<String, Path> partPathMap = new ConcurrentHashMap<>(); @Override protected Path createFile(String filename) throws Exception { Path tempFile = super.createFile(filename); // 生成唯一标识,可通过请求上下文或FilePart的name关联到具体文件 String partUniqueId = UUID.randomUUID().toString(); partPathMap.put(partUniqueId, tempFile); return tempFile; } // 根据唯一标识获取文件Path,使用后移除避免内存泄漏 public Path getFilePath(String partUniqueId) { return partPathMap.remove(partUniqueId); } }
步骤2:配置Multipart消息读取器
将自定义的存储策略注入到SynchronossPartHttpMessageReader中,替换默认的文件处理逻辑:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.codec.multipart.MultipartHttpMessageReader; import org.springframework.http.codec.multipart.SynchronossPartHttpMessageReader; @Configuration public class MultipartConfig { @Bean public MultipartHttpMessageReader multipartHttpMessageReader() { TrackableFileStorageStrategy storageStrategy = new TrackableFileStorageStrategy(); SynchronossPartHttpMessageReader partReader = new SynchronossPartHttpMessageReader(); partReader.setFileStorageStrategy(storageStrategy); return new MultipartHttpMessageReader(partReader); } // 将自定义存储策略暴露为Bean,方便在请求处理时注入使用 @Bean public TrackableFileStorageStrategy trackableFileStorageStrategy() { return new TrackableFileStorageStrategy(); } }
步骤3:在请求处理中获取Path
在处理multipart请求时,通过自定义存储策略获取对应文件的Path:
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; @RestController public class UploadController { private final TrackableFileStorageStrategy storageStrategy; public UploadController(TrackableFileStorageStrategy storageStrategy) { this.storageStrategy = storageStrategy; } @PostMapping("/upload") public Mono<Void> handleUpload(@RequestPart("file") FilePart filePart) { // 这里需要根据实际场景关联partUniqueId与filePart,比如用filePart.name()作为标识 // 注意:如果同一请求有多个同名文件,需要额外处理唯一标识 String partId = filePart.name(); Path file = storageStrategy.getFilePath(partId); // 将file传递给杀毒扫描器处理 return scanFile(file) .then(); } private Mono<Void> scanFile(Path file) { // 调用杀毒扫描器的逻辑 return Mono.empty(); } }
2. 利用临时文件生命周期(不推荐)
Spring WebFlux默认会将上传的文件写入临时目录,且会在请求处理完成后自动清理。如果杀毒扫描器能在清理前完成扫描,可以通过配置spring.codec.multipart.max-in-memory-size=0强制所有文件写入磁盘,但这种方式仍无法通过公开API直接获取Path,可靠性远不如自定义存储策略。
为什么不推荐反射方案?
反射方式完全依赖Spring内部类的私有字段(如DefaultParts的content、FilePartContent的file),一旦Spring版本升级,内部类结构或字段名发生变化,代码会直接失效,同时破坏了封装性,后续维护成本极高。
内容的提问来源于stack exchange,提问作者Bapt_

