Controller Endpoint Configuration
To begin, define a REST endpoint that accepts a MultipartFile object. The controller layer acts as the interface for the client, delegating the actual storage logic to the service layer.
@PostMapping("/api/files/upload")
public ResponseEntity<String> handleAvatarUpload(@RequestParam("imageFile") MultipartFile imageFile) {
String relativePath = fileStorageService.storeFile(imageFile);
return ResponseEntity.ok(relativePath);
}
Service Layer Implementation
The service layer contains the core business logic for processing the upload. This includes validating the file format, generating a unique filename to prevent overwriting exisitng files, and persisting the data to the local filesystem.
public String storeFile(MultipartFile imageFile) {
// 1. Define permitted image formats
Set<String> allowedExtensions = Set.of("jpg", "jpeg", "png", "gif", "bmp");
String originalFilename = imageFile.getOriginalFilename();
String fileExtension = extractExtension(originalFilename).toLowerCase();
// 2. Validate file type
if (!allowedExtensions.contains(fileExtension)) {
log.error("Unsupported file format detected: {}", fileExtension);
throw new UnsupportedMediaTypeException("Invalid image type");
}
// 3. Generate a unique identifier for the file
String fileId = UUID.randomUUID().toString();
String targetFilename = fileId + "." + fileExtension;
// 4. Determine the storage directory (current working directory)
String baseDirectory = System.getProperty("user.dir");
String storageFolder = baseDirectory + File.separator + "user_avatars";
File destination = new File(storageFolder, targetFilename);
// 5. Ensure directories exist
if (!destination.getParentFile().exists()) {
destination.getParentFile().mkdirs();
}
try {
// 6. Transfer the file stream to the destination
imageFile.transferTo(destination);
// Return the relative path for frontend consumption
return File.separator + "user_avatars" + File.separator + targetFilename;
} catch (IOException e) {
log.error("File system write failure for {}", targetFilename, e);
throw new FileStorageException("Failed to save image");
}
}
private String extractExtension(String filename) {
return Optional.ofNullable(filename)
.filter(f -> f.contains("."))
.map(f -> f.substring(filename.lastIndexOf(".") + 1))
.orElse("");
}
While this implementation specifically targets image validation for avatar uploads, it can be adapted for generic file handling by removing the extension vaildation step.