You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot自定义文件校验:如何捕获非CSV文件上传的NotValidFormatException

问题描述

我希望在上传非CSV格式文件时抛出NotValidFormatException异常,目前已实现自定义校验注解、校验器、异常响应类和全局异常处理器,但上传PDF等无效类型文件时,实际抛出的是CSV解析异常:Failed to parse csv file: (line 322) invalid char between encapsulated token and delimiter,无法被自定义的onNotValidFormatException方法捕获。相关实现代码如下:

自定义校验注解 ValidFile

@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Constraint(validatedBy = {FileValidator.class})
public @interface ValidFile {
    //...
}

校验器 FileValidator

public class FileValidator implements ConstraintValidator<ValidFile, MultipartFile> {

    @Override
    public boolean isValid(MultipartFile multipartFile, 
                           ConstraintValidatorContext context) {
        // ... 此处校验逻辑未完善
    }
}

异常响应类 ValidationErrorResponse

public class ValidationErrorResponse {
    // 包含fieldName和response字段
}

全局异常处理器 ErrorHandlingControllerAdvice

@ControllerAdvice
class ErrorHandlingControllerAdvice {

    @ExceptionHandler(NotValidFormatException.class)
    @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
    @ResponseBody
    ValidationErrorResponse onNotValidFormatException(NotValidFormatException e) {
        //...
    }
}

Controller层代码

public ResponseEntity<ResponseMessage> uploadFile(
                @Valid @ValidFile @RequestParam("file") MultipartFile file) 
                throws Exception {
            employeeService.create(file);
            return ResponseEntity.ok(new ResponseMessage("File uploaded."));
    }

尝试过使用@Validated、@RequestPart("file"),均无效果。


解决方案

1. 完善FileValidator的前置校验逻辑

当前校验器未真正拦截非CSV文件,导致无效文件进入Service层触发解析异常。需在isValid方法中提前校验文件格式,不通过时抛出自定义异常:

import org.apache.commons.io.FilenameUtils;
import java.util.Arrays;
import java.util.List;

public class FileValidator implements ConstraintValidator<ValidFile, MultipartFile> {

    private static final List<String> ALLOWED_EXTENSIONS = Arrays.asList("csv");
    private static final List<String> ALLOWED_MIME_TYPES = Arrays.asList(
        "text/csv",
        "application/vnd.ms-excel",
        "application/csv"
    );

    @Override
    public boolean isValid(MultipartFile file, ConstraintValidatorContext context) {
        if (file.isEmpty()) {
            throw new NotValidFormatException("文件不能为空");
        }

        // 校验文件扩展名
        String originalFilename = file.getOriginalFilename();
        String extension = FilenameUtils.getExtension(originalFilename).toLowerCase();
        if (!ALLOWED_EXTENSIONS.contains(extension)) {
            throw new NotValidFormatException("仅支持CSV格式文件");
        }

        // 校验MIME类型
        String contentType = file.getContentType();
        if (!ALLOWED_MIME_TYPES.contains(contentType)) {
            throw new NotValidFormatException("文件MIME类型无效,仅支持CSV");
        }

        return true;
    }
}

注:可引入org.apache.commons.io.FilenameUtils处理扩展名,或自行实现字符串截取逻辑。

2. 处理Service层的CSV解析异常

若前置校验仍有遗漏(比如扩展名正确但内容不是CSV的文件),需在Service层捕获解析异常并转抛NotValidFormatException:

public void create(MultipartFile file) {
    try {
        // 原CSV解析逻辑
        CSVParser parser = new CSVParser(...);
        // ...
    } catch (CSVParseException e) {
        throw new NotValidFormatException("文件内容不是有效的CSV格式", e);
    }
}

3. 补充全局异常处理器(可选)

如果不想修改Service层代码,也可以在ErrorHandlingControllerAdvice中直接捕获CSV解析异常,转成自定义响应:

@ControllerAdvice
class ErrorHandlingControllerAdvice {

    @ExceptionHandler(NotValidFormatException.class)
    @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
    @ResponseBody
    ValidationErrorResponse onNotValidFormatException(NotValidFormatException e) {
        ValidationErrorResponse response = new ValidationErrorResponse();
        response.setFieldName("file");
        response.setResponse(e.getMessage());
        return response;
    }

    // 捕获CSV解析异常,转成自定义响应
    @ExceptionHandler(CSVParseException.class)
    @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
    @ResponseBody
    ValidationErrorResponse onCSVParseException(CSVParseException e) {
        ValidationErrorResponse response = new ValidationErrorResponse();
        response.setFieldName("file");
        response.setResponse("文件内容不是有效的CSV格式");
        return response;
    }
}

4. 确保Controller启用校验

在Controller类上添加@Validated注解,确保方法参数的自定义校验生效:

@RestController
@Validated // 添加此注解
public class FileUploadController {

    public ResponseEntity<ResponseMessage> uploadFile(
            @ValidFile @RequestParam("file") MultipartFile file) 
            throws Exception {
        employeeService.create(file);
        return ResponseEntity.ok(new ResponseMessage("File uploaded."));
    }
}

注:使用@Validated后,@Valid注解可省略,因为@Validated会触发参数校验。


内容的提问来源于stack exchange,提问作者user19601508

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 18:58:34