Spring MVC 文件大小与类型限制
文件上传时需要限制文件大小和类型,防止服务器资源滥用和安全隐患。
配置文件大小限制
application.yml配置
YAML
spring:
servlet:
multipart:
max-file-size: 10MB # 单个文件最大10MB
max-request-size: 100MB # 总请求最大100MB
file-size-threshold: 2KB # 超过2KB写入临时文件
location: /tmp/upload # 临时文件存储位置
application.properties配置
properties
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB
spring.servlet.multipart.file-size-threshold=2KB
spring.servlet.multipart.location=/tmp/upload
配置类方式
Java
@Configuration
public class MultipartConfig {
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize(DataSize.ofMegabytes(10));
factory.setMaxRequestSize(DataSize.ofMegabytes(100));
factory.setFileSizeThreshold(DataSize.ofKilobytes(2));
factory.setLocation("/tmp/upload");
return factory.createMultipartConfig();
}
}
文件大小限制异常处理
Java
@RestControllerAdvice
public class FileUploadExceptionHandler {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ResponseEntity<Map<String, Object>> handleMaxSizeException(
MaxUploadSizeExceededException e) {
Map<String, Object> response = new HashMap<>();
response.put("code", 400);
response.put("message", "文件大小超过限制");
long maxFileSize = e.getMaxFileSize();
response.put("maxSize", maxFileSize / 1024 / 1024 + "MB");
return ResponseEntity.badRequest().body(response);
}
}
文件类型限制
Controller手动校验
Java
@RestController
@RequestMapping("/api/upload")
public class UploadController {
private static final Set<String> ALLOWED_TYPES = Set.of(
"jpg", "jpeg", "png", "gif", "pdf", "doc", "docx"
);
private static final Map<String, List<String>> TYPE_EXTENSIONS = Map.of(
"image", List.of("jpg", "jpeg", "png", "gif"),
"document", List.of("pdf", "doc", "docx", "xls", "xlsx"),
"video", List.of("mp4", "avi", "mov")
);
@PostMapping("/restricted")
public String uploadRestricted(@RequestParam("file") MultipartFile file) {
validateFile(file);
return saveFile(file);
}
private void validateFile(MultipartFile file) {
if (file.isEmpty()) {
throw new ValidationException("文件为空");
}
// 校验文件大小
if (file.getSize() > 10 * 1024 * 1024) {
throw new ValidationException("文件大小不能超过10MB");
}
// 校验文件扩展名
String extension = getExtension(file.getOriginalFilename());
if (!ALLOWED_TYPES.contains(extension.toLowerCase())) {
throw new ValidationException("不允许的文件类型: " + extension);
}
// 校验ContentType
String contentType = file.getContentType();
if (!isValidContentType(contentType)) {
throw new ValidationException("不允许的ContentType: " + contentType);
}
}
private String getExtension(String fileName) {
if (fileName == null || !fileName.contains(".")) {
return "";
}
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
private boolean isValidContentType(String contentType) {
if (contentType == null) {
return false;
}
return contentType.startsWith("image/") ||
contentType.equals("application/pdf") ||
contentType.startsWith("application/msword") ||
contentType.startsWith("application/vnd.");
}
}
自定义校验注解
Java
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FileSizeValidator.class)
public @interface MaxFileSize {
String message() default "文件大小超过限制";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
long maxSize() default 10 * 1024 * 1024; // 默认10MB
}
public class FileSizeValidator implements ConstraintValidator<MaxFileSize, MultipartFile> {
private long maxSize;
@Override
public void initialize(MaxFileSize constraintAnnotation) {
this.maxSize = constraintAnnotation.maxSize();
}
@Override
public boolean isValid(MultipartFile file, ConstraintValidatorContext context) {
if (file == null || file.isEmpty()) {
return true;
}
return file.getSize() <= maxSize;
}
}
文件类型校验注解
Java
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FileTypeValidator.class)
public @interface AllowedFileTypes {
String message() default "不允许的文件类型";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String[] types() default {};
}
public class FileTypeValidator implements ConstraintValidator<AllowedFileTypes, MultipartFile> {
private Set<String> allowedTypes;
@Override
public void initialize(AllowedFileTypes constraintAnnotation) {
this.allowedTypes = Set.of(constraintAnnotation.types());
}
@Override
public boolean isValid(MultipartFile file, ConstraintValidatorContext context) {
if (file == null || file.isEmpty()) {
return true;
}
String extension = getExtension(file.getOriginalFilename());
return allowedTypes.contains(extension.toLowerCase());
}
private String getExtension(String fileName) {
if (fileName == null || !fileName.contains(".")) {
return "";
}
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
}
使用校验注解
Java
@PostMapping("/validated")
public String uploadValidated(
@MaxFileSize(maxSize = 5 * 1024 * 1024, message = "文件不能超过5MB")
@AllowedFileTypes(types = {"jpg", "png", "gif"}, message = "只允许图片文件")
@RequestParam("file") MultipartFile file) {
return saveFile(file);
}
文件内容校验
Java
private void validateFileContent(MultipartFile file) {
try {
InputStream inputStream = file.getInputStream();
// 校验图片文件
if (isImageFile(file)) {
BufferedImage image = ImageIO.read(inputStream);
if (image == null) {
throw new ValidationException("图片内容无效");
}
}
// 校验PDF文件
if (isPdfFile(file)) {
PDFParser parser = new PDFParser(inputStream);
parser.parse();
// ...PDF内容校验
}
} catch (IOException e) {
throw new ValidationException("文件内容校验失败");
}
}
private boolean isImageFile(MultipartFile file) {
String contentType = file.getContentType();
return contentType != null && contentType.startsWith("image/");
}
文件类型白名单
Java
@Component
public class FileTypeWhitelist {
private final Map<String, FileTypeConfig> configs;
public FileTypeWhitelist() {
configs = Map.of(
"avatar", new FileTypeConfig(
List.of("jpg", "jpeg", "png"),
2 * 1024 * 1024,
List.of("image/jpeg", "image/png")
),
"document", new FileTypeConfig(
List.of("pdf", "doc", "docx", "xls", "xlsx"),
20 * 1024 * 1024,
List.of("application/pdf", "application/msword")
),
"video", new FileTypeConfig(
List.of("mp4", "avi"),
100 * 1024 * 1024,
List.of("video/mp4", "video/avi")
)
);
}
public boolean isAllowed(String category, String extension) {
FileTypeConfig config = configs.get(category);
return config != null && config.extensions.contains(extension.toLowerCase());
}
public boolean isSizeAllowed(String category, long size) {
FileTypeConfig config = configs.get(category);
return config != null && size <= config.maxSize;
}
}
@Data
@AllArgsConstructor
class FileTypeConfig {
private List<String> extensions;
private long maxSize;
private List<String> contentTypes;
}
配置限制对比
| 配置项 | 说明 | 默认值 |
|---|---|---|
| max-file-size | 单文件最大大小 | 1MB |
| max-request-size | 请求总大小 | 10MB |
| file-size-threshold | 内存阈值 | 0 |
| location | 临时文件目录 | 系统临时目录 |
MIME类型参考
| 文件类型 | MIME类型 |
|---|---|
| jpg/jpeg | image/jpeg |
| png | image/png |
| gif | image/gif |
| application/pdf | |
| doc | application/msword |
| docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
| xls | application/vnd.ms-excel |
| xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
| mp4 | video/mp4 |
| txt | text/plain |
要点总结
- spring.servlet.multipart配置文件大小限制
- MaxUploadSizeExceededException处理超限异常
- 手动校验文件扩展名和ContentType
- 自定义校验注解可声明式配置限制
- 白名单机制统一管理文件类型限制
- 内容校验确保文件真实类型匹配
📝 发现内容有误?点击此处直接编辑