在Spring Boot中,你可以通过以下几种方式来校验上传的文件是否为Excel文件:

方案一:基于文件扩展名和Content-Type校验

java

@PostMapping("/uploadAndParseExcelFile")
@Debounce(key = "uploadAndParseExcelFile", value = 10000)
public Result>> uploadAndParseExcelFile(@RequestParam("uploadFile") MultipartFile uploadFile) {
    log.info("【上传Excel文件】,解析Excel文件,/uploadAndParseExcelFile,uploadFile = {}", uploadFile);
    
    // 校验文件是否为空
    if (uploadFile.isEmpty()) {
        return Result.error("请选择要上传的文件");
    }
    
    // 校验文件类型
    if (!isExcelFile(uploadFile)) {
        return Result.error("请上传有效的Excel文件(.xls 或 .xlsx 格式)");
    }
    
    List> data = fileUploadService.uploadAndParseExcelFile(uploadFile);
    return Result.success(data);
}

/**
 * 校验是否为Excel文件
 * @param file 上传的文件
 * @return 是否为Excel文件
 */
private boolean isExcelFile(MultipartFile file) {
    // 获取原始文件名
    String originalFilename = file.getOriginalFilename();
    if (originalFilename == null) {
        return false;
    }
    
    // 校验文件扩展名
    String extension = getFileExtension(originalFilename).toLowerCase();
    if (!Arrays.asList("xls", "xlsx").contains(extension)) {
        return false;
    }
    
    // 校验Content-Type(可选,增强校验)
    String contentType = file.getContentType();
    if (contentType != null) {
        List excelContentTypes = Arrays.asList(
            "application/vnd.ms-excel", // .xls
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" // .xlsx
        );
        if (!excelContentTypes.contains(contentType)) {
            return false;
        }
    }
    
    return true;
}

/**
 * 获取文件扩展名
 * @param filename 文件名
 * @return 文件扩展名
 */
private String getFileExtension(String filename) {
    int lastDotIndex = filename.lastIndexOf(".");
    if (lastDotIndex == -1) {
        return "";
    }
    return filename.substring(lastDotIndex + 1);
}

方案二:使用注解校验(推荐)

首先创建自定义校验注解:

java

/**
 * Excel文件校验注解
 */
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ExcelFileValidator.class)
public @interface ValidExcelFile {
    String message() default "请上传有效的Excel文件(.xls 或 .xlsx 格式)";
    Class[] groups() default {};
    Class[] payload() default {};
}

创建校验器:

java

/**
 * Excel文件校验器
 */
public class ExcelFileValidator implements ConstraintValidator {
    
    private static final List EXCEL_EXTENSIONS = Arrays.asList("xls", "xlsx");
    private static final List EXCEL_CONTENT_TYPES = Arrays.asList(
        "application/vnd.ms-excel",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
    );
    
    @Override
    public boolean isValid(MultipartFile file, ConstraintValidatorContext context) {
        if (file == null || file.isEmpty()) {
            return false;
        }
        
        // 校验文件名
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null) {
            return false;
        }
        
        // 校验扩展名
        String extension = getFileExtension(originalFilename).toLowerCase();
        if (!EXCEL_EXTENSIONS.contains(extension)) {
            return false;
        }
        
        // 校验Content-Type
        String contentType = file.getContentType();
        if (contentType != null && !EXCEL_CONTENT_TYPES.contains(contentType)) {
            return false;
        }
        
        return true;
    }
    
    private String getFileExtension(String filename) {
        int lastDotIndex = filename.lastIndexOf(".");
        return lastDotIndex == -1 ? "" : filename.substring(lastDotIndex + 1);
    }
}

然后在Controller中使用:

java

@PostMapping("/uploadAndParseExcelFile")
@Debounce(key = "uploadAndParseExcelFile", value = 10000)
public Result>> uploadAndParseExcelFile(
        @ValidExcelFile @RequestParam("uploadFile") MultipartFile uploadFile) {
    log.info("【上传Excel文件】,解析Excel文件,/uploadAndParseExcelFile,uploadFile = {}", uploadFile);
    
    List> data = fileUploadService.uploadAndParseExcelFile(uploadFile);
    return Result.success(data);
}

方案三:使用全局异常处理

创建全局异常处理器:

java

@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public Result handleConstraintViolationException(ConstraintViolationException e) {
        return Result.error(e.getMessage());
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        String errorMessage = e.getBindingResult().getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining(", "));
        return Result.error(errorMessage);
    }
}

方案四:在Service层进行校验

你也可以在Service层进行更严格的校验:

java

@Service
public class FileUploadService {
    
    public List> uploadAndParseExcelFile(MultipartFile uploadFile) {
        // 校验文件
        validateExcelFile(uploadFile);
        
        // 处理文件的逻辑...
    }
    
    private void validateExcelFile(MultipartFile file) {
        if (file.isEmpty()) {
            throw new IllegalArgumentException("文件不能为空");
        }
        
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null) {
            throw new IllegalArgumentException("文件名不能为空");
        }
        
        String extension = getFileExtension(originalFilename).toLowerCase();
        if (!Arrays.asList("xls", "xlsx").contains(extension)) {
            throw new IllegalArgumentException("只支持 .xls 和 .xlsx 格式的Excel文件");
        }
        
        // 还可以添加文件大小校验
        if (file.getSize() > 10 * 1024 * 1024) { // 10MB
            throw new IllegalArgumentException("文件大小不能超过10MB");
        }
    }
    
    private String getFileExtension(String filename) {
        int lastDotIndex = filename.lastIndexOf(".");
        return lastDotIndex == -1 ? "" : filename.substring(lastDotIndex + 1);
    }
}

推荐使用方案

推荐使用方案二(注解校验),因为:

  1. 代码更清晰,职责分离

  2. 可复用性强

  3. 与Spring的校验框架集成良好

  4. 易于维护和扩展

选择哪种方案取决于你的具体需求和项目架构。如果只是简单的校验,方案一就足够了;如果需要更好的代码结构和可复用性,建议使用方案二。


实例代码:

1、使用注解 @ValidExcelFile 校验是否Excel文件

src/main/java/com/weiyu/controller/FileUploadController.java

    /**
     * 上传解析Excel文件,返回解析后的列表数据
     * 

* 因为前端是用 "Content-Type": "multipart/form-data" 的方式发送的请求,这里就不能用 @RequestBody,而是用 MultipartFile * 并且形参名称 uploadFile 需要与前端定义的保持一致 * * @param uploadFile 上传需要解析的Excel文件,结构见 {@link MultipartFile} * @return 列表数据 {@link Result}<{@link List}<{@link Map}<{@link String},{@link Object}>>> * @apiNote 本接口使用防抖机制,默认 10s 内重复请求会被忽略 */ @PostMapping("/uploadAndParseExcelFile") @Debounce(key = "uploadAndParseExcelFile", value = 10000) // @ResponseBody // 直接序列化返回值,使用 Result>> 替换 ResponseEntity public Result>> uploadAndParseExcelFile(@ValidExcelFile MultipartFile uploadFile) { log.info("【上传Excel文件】,解析Excel文件,/uploadAndParseExcelFile,uploadFile = {}", uploadFile); List> data = fileUploadService.uploadAndParseExcelFile(uploadFile); return Result.success(data); }

2、新建注解 @ValidExcelFile

src/main/java/com/weiyu/annotation/ValidExcelFile.java

package com.weiyu.annotation;
import com.weiyu.validation.ExcelFileValidator;
import jakarta.validation.Constraint;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * Excel文件校验注解
 */
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ExcelFileValidator.class)
public @interface ValidExcelFile {
    String message() default "请上传有效的Excel文件(.xls 或 .xlsx 格式)";
}

3、新建 Excel文件校验器

src/main/java/com/weiyu/validation/ExcelFileValidator.java

package com.weiyu.validation;
import com.weiyu.annotation.ValidExcelFile;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.web.multipart.MultipartFile;
import java.util.Arrays;
import java.util.List;
/**
 * Excel文件校验器
 */
public class ExcelFileValidator implements ConstraintValidator {
    private static final List EXCEL_EXTENSIONS = Arrays.asList("xls", "xlsx");
    private static final List EXCEL_CONTENT_TYPES = Arrays.asList(
            "application/vnd.ms-excel",
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
    );
    @Override
    public boolean isValid(MultipartFile file, ConstraintValidatorContext context) {
        if (file == null || file.isEmpty()) {
            return false;
        }
        // 校验文件名
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null) {
            return false;
        }
        // 校验扩展名
        String extension = getFileExtension(originalFilename).toLowerCase();
        if (!EXCEL_EXTENSIONS.contains(extension)) {
            return false;
        }
        // 校验Content-Type
        String contentType = file.getContentType();
        return contentType == null || EXCEL_CONTENT_TYPES.contains(contentType);
    }
    private String getFileExtension(String filename) {
        int lastDotIndex = filename.lastIndexOf(".");
        return lastDotIndex == -1 ? "" : filename.substring(lastDotIndex + 1);
    }
}