方式 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(
@requestpartjson)- 复杂数据结构
根据实际业务需求选择合适的方式,优先考虑代码的可维护性和可读性!
到此这篇关于spring boot 处理带文件表单的方式汇总的文章就介绍到这了,更多相关springboot文件表单内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论