在 spring boot 中校验文件类型,核心做法是结合 `@validated`、自定义注解和 `validator` 实现,通过检查文件的 mime 类型或扩展名来限制上传格式。具体而言,你需要定义一个包含白名单的注解,并在 `constraintvalidator` 中读取 `multipartfile` 的内容或使用 `files.probecontenttype()` 获取真实类型,从而拦截不符合要求的文件。

如何定义支持白名单的文件类型校验注解
直接回答:通过创建继承 `ConstraintAnnotation` 的接口并实现 `ConstraintValidator`,可以灵活地指定允许的文件后缀或 MIME 类型。这种方式的优点是校验逻辑与业务代码解耦,且能复用。

import org.springframework.web.multipart.MultipartFile;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class ValidFileExtensionValidator implements ConstraintValidator<validfile multipartfile> {
private String[] allowedExtensions;
@Override
public void initialize(ValidFile constraintAnnotation) {
this.allowedExtensions = constraintAnnotation.value();
}
@Override
public boolean isValid(MultipartFile file, ConstraintValidatorContext context) {
if (file == null || file.isEmpty()) {
return true; // 由 @NotNull 或 @NotBlank 处理空值
}
String originalFilename = file.getOriginalFilename();
if (originalFilename == null) {
return false;
}
String extension = getFileExtension(originalFilename).toLowerCase();
for (String ext : allowedExtensions) {
if (ext.equalsIgnoreCase(extension)) {
return true;
}
}
return false;
}
private String getFileExtension(String filename) {
int dotIndex = filename.lastIndexOf('.');
return (dotIndex == -1) ? "" : filename.substring(dotIndex + 1);
}
}</validfile>
如何在 Controller 层集成并使用该校验器
直接回答:在 Controller 的方法参数前使用 `@Valid` 和自定义注解,Spring MVC 会自动触发校验。如果校验失败,会抛出 `MethodArgumentNotValidException`,你可以通过全局异常处理器返回统一的错误信息。

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/files")
public class FileUploadController {
@PostMapping("/upload")
public ResponseEntity<map object>> uploadFile(
@RequestParam("file") @Valid @ValidFile({"jpg", "png", "pdf"}) MultipartFile file) {
Map<string object> response = new HashMap();
response.put("message", "File uploaded successfully");
response.put("filename", file.getOriginalFilename());
return ResponseEntity.ok(response);
}
}
// 全局异常处理示例
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<map string>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<string string> errors = new HashMap();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage()));
return ResponseEntity.badRequest().body(errors);
}
}</string></map></string></map>
当文件没有扩展名时校验器如何处理
直接回答:当文件没有扩展名时,上述实现的 `isValid` 方法会返回 `false`,因为 `getFileExtension` 方法在找不到点号时返回空字符串,而空字符串通常不在允许的白名单中。这确保了只有明确指定了合法扩展名的文件才能通过校验,防止了无扩展名文件的上传。











