I am using spring boot 3, java 21 for a rest api.
One of the API will handle file upload. Files can be pretty big, 2~3GB. Reading the spring documentation, I read that the MultipartFile
request will always either save the data to memory or save it as a temporary file. Either way does not work. If possible I want a way to directly stream the MultipartFile
data as it is being uploaded. I have read some SO posts, some of them is using Apache commons fileupload stream. Those posts are pretty old.
I will share two rest controller methods.
@PostMapping("/{fileId}/content")@PreAuthorize("hasAuthority('SCOPE_write:files')")public ResponseEntity<String> postFileFileId(@PathVariable Long fileId, @RequestParam("file") MultipartFile multipartFile) { Optional<FileResourceEntity> fileResourceEntityOptional = fileRepository.findByFileIdAndIsTrashFalse(fileId); if (!fileResourceEntityOptional.isPresent()) { return ResponseEntity.notFound().build(); } try { FileResourceEntity fileResourceEntity = fileResourceEntityOptional.get(); createFoldersAndSaveUploadFile(fileResourceEntity, multipartFile); fileRepository.save(fileResourceEntity); return ResponseEntity.noContent().build(); } catch (Exception ex) { return ResponseEntity.badRequest().body(UPLOAD_FAILED_MESSAGE); }}@PostMapping(consumes = { "multipart/form-data" })@PreAuthorize("hasAnyAuthority('SCOPE_write:files')")public ResponseEntity<String> postFile( @RequestPart("filedetail") FileResourceDetail fileResource, @RequestPart("content") MultipartFile multipartFile, UriComponentsBuilder ubc) { FileResourceEntity savedFile = new FileResourceEntity(); try { FileResourceEntity fileData = fileResourceDTOService.convertToEntity(fileResource); savedFile = fileRepository.save(fileData); if (!multipartFile.isEmpty()) { createFoldersAndSaveUploadFile(savedFile, multipartFile); } URI locationOfNewFile = ubc .path("/api/v4/files/{fileId}") .buildAndExpand(savedFile.getFileId()) .toUri(); return ResponseEntity.created(locationOfNewFile).build(); } catch (Exception ex) { fileRepository.deleteById(savedFile.getFileId()); return ResponseEntity.badRequest() .body(UPLOAD_FAILED_MESSAGE); }}private void createFoldersAndSaveUploadFile(FileResourceEntity fileResourceEntity, MultipartFile multipartFile) throws IOException { String createDate = fileResourceEntity.getCreatedAt().format(DATE_FORMATTER); createFolders(Path.of(rootDataFolder, createDate)); Files.copy(multipartFile.getInputStream(), getFilePath(fileResourceEntity));}
We are running the application as pod in Kubernetes. Files are saved to a PV, which is actually mounted FSX. Right now, after client finishes upload, server takes a long time to save the multipart file to actual file server, then returns response.
Is there any way to directly save/stream uploads to file using Spring Boot?