Spring Boot 处理带文件表单的方式汇总

 更新时间:2025年12月12日 11:54:51   作者:zwjapple  
本文详细介绍了六种处理文件上传的方式,包括@RequestParam、@RequestPart、@ModelAttribute、@ModelAttribute+@RequestPart、@RequestPart接收JSON和HttpServletRequest原生方式,每种方式都有其特点和适用场景,感兴趣的朋友跟随小编一起看看吧

方式 1:@RequestParam接收文件

后端代码

@PostMapping("/upload1")
public Result upload1(
    @RequestParam("file") MultipartFile file,
    @RequestParam("name") String name,
    @RequestParam("age") Integer age
) {
    // 处理文件
    String filename = file.getOriginalFilename();
    long size = file.getSize();
    return Result.success();
}

前端代码

const formData = new FormData();
formData.append('file', fileBlob);
formData.append('name', 'John');
formData.append('age', '25');
fetch('/api/upload1', {
    method: 'POST',
    body: formData
});

特点

  • ✅ 最简单直接
  • ✅ 适合少量参数
  • ❌ 参数多时代码冗长
  • ❌ 无法使用 @Valid 统一验证

方式 2:@RequestPart接收文件

后端代码

@PostMapping("/upload2")
public Result upload2(
    @RequestPart("file") MultipartFile file,
    @RequestPart("name") String name,
    @RequestPart("age") Integer age
) {
    return Result.success();
}

前端代码

// 与方式1相同
const formData = new FormData();
formData.append('file', fileBlob);
formData.append('name', 'John');
formData.append('age', '25');

特点

  • ✅ 语义更明确(专为 multipart 设计)
  • ✅ 可以指定每个 part 的 Content-Type
  • ❌ 与 @RequestParam 功能类似,参数多时也冗长

方式 3:@ModelAttribute接收(文件在 DTO 中)

后端代码

@Data
public class UploadDTO {
    @NotNull(message = "文件不能为空")
    private MultipartFile file;
    @NotBlank(message = "姓名不能为空")
    private String name;
    @NotNull(message = "年龄不能为空")
    @Min(value = 0, message = "年龄不能为负数")
    private Integer age;
    private String description;
}
@PostMapping("/upload3")
public Result upload3(@Valid @ModelAttribute UploadDTO dto) {
    MultipartFile file = dto.getFile();
    // 额外检查文件是否为空
    if (file.isEmpty()) {
        throw new BusinessException("文件内容不能为空");
    }
    return Result.success();
}

前端代码

const formData = new FormData();
formData.append('file', fileBlob);
formData.append('name', 'John');
formData.append('age', '25');
formData.append('description', '这是描述');
fetch('/api/upload3', {
    method: 'POST',
    body: formData
});

特点

  • ✅ 代码简洁,参数封装在 DTO 中
  • ✅ 支持 @Valid 统一验证
  • ✅ DTO 可复用
  • ✅ 适合传统表单提交
  • ❌ 只支持扁平数据结构

方式 4:@ModelAttribute+@RequestPart混合(推荐)

后端代码

@Data
public class LoanRequest {
    @NotBlank(message = "姓名不能为空")
    private String name;
    @NotNull(message = "贷款金额不能为空")
    private BigDecimal amount;
    @NotBlank(message = "贷款类型不能为空")
    private String loanType;
    private String description;
}
@PostMapping("/upload4")
public Result upload4(
    @Valid @ModelAttribute LoanRequest request,
    @RequestPart(value = "propProofDocs", required = false) MultipartFile file
) {
    // 文件验证
    if (file == null || file.isEmpty()) {
        throw new BusinessException("财产证明文件不能为空");
    }
    return Result.success();
}

前端代码

const formData = new FormData();
// 普通字段
formData.append('name', 'John');
formData.append('amount', '100000');
formData.append('loanType', 'mortgage');
formData.append('description', '购房贷款');
// 文件单独处理
formData.append('propProofDocs', fileBlob);
fetch('/api/upload4', {
    method: 'POST',
    body: formData
});

特点

  • 最灵活,业务数据和文件分离
  • ✅ 支持 DTO 验证
  • ✅ 文件验证可以单独处理
  • ✅ 适合复杂业务场景
  • 推荐用于大多数项目

方式 5:@RequestPart接收 JSON + 文件

后端代码

@Data
public class UserInfo {
    @NotBlank(message = "姓名不能为空")
    private String name;
    @NotNull(message = "年龄不能为空")
    private Integer age;
    private List<String> hobbies;  // 支持复杂结构
    private Address address;  // 支持嵌套对象
}
@Data
class Address {
    private String city;
    private String street;
}
@PostMapping("/upload5")
public Result upload5(
    @Valid @RequestPart("userInfo") UserInfo userInfo,
    @RequestPart("avatar") MultipartFile avatar
) {
    return Result.success();
}

前端代码

const userInfo = {
    name: 'John',
    age: 25,
    hobbies: ['reading', 'coding'],
    address: {
        city: 'Beijing',
        street: 'Chang\'an Ave'
    }
};
const formData = new FormData();
// 将 JSON 对象转为 Blob,指定 Content-Type
formData.append('userInfo', new Blob([JSON.stringify(userInfo)], {
    type: 'application/json'
}));
formData.append('avatar', fileBlob);
fetch('/api/upload5', {
    method: 'POST',
    body: formData
});

特点

  • ✅ 支持复杂 JSON 结构(数组、嵌套对象)
  • ✅ 前后端分离友好
  • ✅ RESTful 风格
  • ❌ 前端需要额外构造 JSON Blob
  • 推荐用于复杂数据结构

方式 6:多文件上传

后端代码

// 方式 6.1:数组接收
@PostMapping("/upload6-1")
public Result upload6(
    @RequestParam("files") MultipartFile[] files,
    @RequestParam("description") String description
) {
    for (MultipartFile file : files) {
        // 处理每个文件
    }
    return Result.success();
}
// 方式 6.2:List 接收
@PostMapping("/upload6-2")
public Result upload6(
    @RequestParam("files") List<MultipartFile> files,
    @RequestParam("description") String description
) {
    files.forEach(file -> {
        // 处理每个文件
    });
    return Result.success();
}
// 方式 6.3:不同字段多个文件
@PostMapping("/upload6-3")
public Result upload6(
    @RequestPart("idCard") MultipartFile idCard,
    @RequestPart("bankCard") MultipartFile bankCard,
    @RequestPart("propProof") MultipartFile propProof
) {
    return Result.success();
}

前端代码

// 多个文件同名
const formData = new FormData();
files.forEach(file => {
    formData.append('files', file);  // 注意:相同的 key
});
formData.append('description', '批量上传');
// 多个文件不同名
const formData2 = new FormData();
formData2.append('idCard', idCardFile);
formData2.append('bankCard', bankCardFile);
formData2.append('propProof', propProofFile);

方式 7:HttpServletRequest 原生方式

后端代码

@PostMapping("/upload7")
public Result upload7(HttpServletRequest request) throws Exception {
    // 判断是否为 multipart 请求
    if (!ServletFileUpload.isMultipartContent(request)) {
        throw new BusinessException("请求格式错误");
    }
    // 使用 Apache Commons FileUpload
    ServletFileUpload upload = new ServletFileUpload();
    List<FileItem> items = upload.parseRequest(new ServletRequestContext(request));
    for (FileItem item : items) {
        if (item.isFormField()) {
            // 普通字段
            String fieldName = item.getFieldName();
            String value = item.getString("UTF-8");
        } else {
            // 文件字段
            String filename = item.getName();
            InputStream inputStream = item.getInputStream();
            // 处理文件
        }
    }
    return Result.success();
}

特点

  • ✅ 完全控制
  • ✅ 适合特殊需求
  • ❌ 代码复杂
  • ❌ 不推荐常规使用

完整对比表

方式适用场景代码简洁度验证支持复杂结构推荐度
@RequestParam简单场景,少量参数⭐⭐⭐⭐
@RequestPart与 @RequestParam 类似⭐⭐⭐⭐
@ModelAttribute(文件在DTO)传统表单,扁平数据⭐⭐⭐⭐⭐⭐⭐⭐
@ModelAttribute + @RequestPart通用场景⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
@RequestPart (JSON)复杂JSON结构⭐⭐⭐⭐⭐⭐⭐
多文件上传批量上传⭐⭐⭐⭐⭐⭐⭐
HttpServletRequest特殊需求

实际项目推荐

场景 1:简单表单 + 单文件

@PostMapping("/simple")
public Result simple(
    @Valid @ModelAttribute SimpleDTO dto,
    @RequestPart("file") MultipartFile file
) { }

场景 2:复杂业务数据 + 多文件

@PostMapping("/complex")
public Result complex(
    @Valid @RequestPart("data") ComplexDTO data,  // JSON
    @RequestPart("idCard") MultipartFile idCard,
    @RequestPart("bankCard") MultipartFile bankCard
) { }

场景 3:可选文件 + 表单验证

@PostMapping("/optional")
public Result optional(
    @Valid @ModelAttribute FormDTO form,
    @RequestPart(value = "attachment", required = false) MultipartFile file
) {
    if (file != null && !file.isEmpty()) {
        // 处理文件
    }
}

文件验证最佳实践

自定义验证注解

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FileValidator.class)
public @interface ValidFile {
    String message() default "文件不合法";
    long maxSize() default 10 * 1024 * 1024;  // 10MB
    String[] allowedTypes() default {};  // {"image/jpeg", "image/png"}
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
// 验证器
public class FileValidator implements ConstraintValidator<ValidFile, MultipartFile> {
    private long maxSize;
    private String[] allowedTypes;
    @Override
    public void initialize(ValidFile annotation) {
        this.maxSize = annotation.maxSize();
        this.allowedTypes = annotation.allowedTypes();
    }
    @Override
    public boolean isValid(MultipartFile file, ConstraintValidatorContext context) {
        if (file == null || file.isEmpty()) {
            return false;
        }
        // 检查大小
        if (file.getSize() > maxSize) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(
                "文件大小不能超过 " + (maxSize / 1024 / 1024) + "MB"
            ).addConstraintViolation();
            return false;
        }
        // 检查类型
        if (allowedTypes.length > 0) {
            String contentType = file.getContentType();
            boolean typeMatched = Arrays.asList(allowedTypes).contains(contentType);
            if (!typeMatched) {
                context.disableDefaultConstraintViolation();
                context.buildConstraintViolationWithTemplate(
                    "只支持以下文件类型: " + String.join(", ", allowedTypes)
                ).addConstraintViolation();
                return false;
            }
        }
        return true;
    }
}
// 使用
@PostMapping("/validated")
public Result validated(
    @Valid @ModelAttribute FormDTO form,
    @ValidFile(
        maxSize = 5 * 1024 * 1024,  // 5MB
        allowedTypes = {"image/jpeg", "image/png", "application/pdf"},
        message = "文件不符合要求"
    )
    @RequestPart("file") MultipartFile file
) {
    return Result.success();
}

总结

日常开发推荐

  • 🥇 方式 4@ModelAttribute + @RequestPart)- 最灵活通用
  • 🥈 方式 3@ModelAttribute 包含文件)- 简单场景
  • 🥉 方式 5@RequestPart JSON)- 复杂数据结构

根据实际业务需求选择合适的方式,优先考虑代码的可维护性和可读性!

到此这篇关于Spring Boot 处理带文件表单的方式汇总的文章就介绍到这了,更多相关springboot文件表单内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于RocketMQ实现分布式事务的方法

    基于RocketMQ实现分布式事务的方法

    了保证系统数据的一致性,我们需要确保这些服务中的操作要么全部成功,要么全部失败,通过使用RocketMQ实现分布式事务,我们可以协调这些服务的操作,保证数据的一致性,这篇文章主要介绍了基于RocketMQ实现分布式事务,需要的朋友可以参考下
    2024-03-03
  • Spring Boot整合Spring Cache及Redis过程解析

    Spring Boot整合Spring Cache及Redis过程解析

    这篇文章主要介绍了Spring Boot整合Spring Cache及Redis过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • SpringBoot条件注解之@ConditionalOnClass等注解的使用场景分析

    SpringBoot条件注解之@ConditionalOnClass等注解的使用场景分析

    文章详细介绍了SpringBoot中条件注解的体系,包括基本概念、@ConditionalOnClass等常用注解的工作原理和使用场景,文章还探讨了条件注解的组合使用、实战应用以及最佳实践,帮助开发者更好地理解和应用条件注解,实现更灵活和智能的应用配置,感兴趣的朋友一起看看吧
    2025-03-03
  • 基于JDK动态代理原理解析

    基于JDK动态代理原理解析

    这篇文章主要介绍了基于JDK动态代理原理解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • Spring Boot之AOP配自定义注解的最佳实践过程

    Spring Boot之AOP配自定义注解的最佳实践过程

    这篇文章主要给大家介绍了关于Spring Boot之AOP配自定义注解的最佳实践过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11
  • Java如何获取文件夹下所有压缩包下指定文件

    Java如何获取文件夹下所有压缩包下指定文件

    在Java中,通过遍历文件夹并对压缩包进行解析,可以实现提取指定文件的功能,如文档、PDF等,该过程中可增加过滤条件来适应不同需求,例如文件类型或文件名过滤,该方法适用于处理大量数据时的文件管理和数据提取
    2024-09-09
  • Spring Boot 使用 logback、logstash、ELK 记录日志文件的方法

    Spring Boot 使用 logback、logstash、ELK 记录日志文件的方法

    这篇文章主要介绍了Spring Boot 使用 logback、logstash、ELK 记录日志文件的思路详解,文中给大家提到了logback 取代 log4j的理由,需要的朋友可以参考下
    2017-12-12
  • Nacos1.4.0 Windows10单机模式启动和集群启动过程解析

    Nacos1.4.0 Windows10单机模式启动和集群启动过程解析

    这篇文章主要介绍了Nacos1.4.0 Windows10单机模式启动和集群启动,第一次使用nacos,废话不多说,记录下自己启动Nacos遇到的坑,感兴趣的朋友跟随小编一起看看吧
    2023-10-10
  • SpringBoot整合RabbitMQ消息队列的完整步骤

    SpringBoot整合RabbitMQ消息队列的完整步骤

    这篇文章主要给大家介绍了关于SpringBoot整合RabbitMQ消息队列的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • SpringBoot循环依赖全场景解析与终极解决方案

    SpringBoot循环依赖全场景解析与终极解决方案

    这篇文章主要为大家详细介绍了SpringBoot循环依赖全场景解析与终极解决方案,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-06-06

最新评论