文章目录▼CloseOpen
- 前端Ajax传文件:FormData+请求头,这两个设置不能忘
- 后端SpringBoot:跨域配置+文件接收接口,一步都不能少
- 第一步:全局CORS配置,解决跨域问题
- 第二步:写文件接收接口,处理细节才不会踩坑
- 前端FormData的key和后端接口参数名不一致会有什么问题?
- SpringBoot跨域配置中allowedOrigins可以写通配符吗?
- 前端用axios代替jQuery Ajax传文件,配置有什么不同?
- 如何用Ajax上传多个文件?
- 修改SpringBoot的文件大小限制后还是上传失败,可能是什么原因?
前端Ajax传文件:FormData+请求头,这两个设置不能忘
你肯定遇到过这种情况:Ajax请求发出去了,后端却反馈“没收到文件”——90%的问题出在FormData和请求头配置上。我先给你讲最核心的步骤,再告诉你我踩过的坑。
构造FormData对象。前端要传文件,必须用FormData把文件“包”起来,比如:
let fileInput = document.getElementById('fileInput');
let formData = new FormData();
formData.append('file', fileInput.files[0]); // 这里的'file'要和后端参数名一致!
我之前帮老张的时候,他前端写的是uploadFile
,后端接口参数是file
,结果文件根本没传过去,改一致就好了——这是最容易犯的低级错误。
然后是Ajax的配置,这两个参数必须加:
$.ajax({
url: 'http://localhost:8081/upload',
type: 'POST',
data: formData,
processData: false, // 别把FormData转成字符串
contentType: false, // 让浏览器自动设置Content-Type为multipart/form-data
success: function(res) {
console.log('上传成功', res);
},
error: function(err) {
console.log('报错了', err);
}
});
为什么要加这两个?因为Ajax默认会把数据转成application/x-www-form-urlencoded
格式(比如表单提交的键值对),但FormData需要用multipart/form-data
格式(专门传文件的)。我之前犯过这个错,没加这两个属性,结果请求发出去了,后端拿到的MultipartFile
是空的,查了半小时资料才反应过来。
还有跨域的“预检请求”(OPTIONS请求)——不用慌,浏览器会自动发这个请求确认后端是否允许跨域,前端不用额外设置,但后端必须处理这个请求(后面会讲)。比如你前端是http://localhost:8080
,后端要允许这个域名的OPTIONS请求,否则会报错“预检请求失败”。
后端SpringBoot:跨域配置+文件接收接口,一步都不能少
后端的活儿分两块:解决跨域和接收文件。我 你用全局跨域配置(比@CrossOrigin
注解灵活),再配合标准的文件接收逻辑。
第一步:全局CORS配置,解决跨域问题
老张之前用@CrossOrigin
注解,但没设置allowedHeaders
,结果前端发的Content-Type
头不被允许,还是跨域报错。后来我帮他写了个全局配置类,直接搞定:
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/") // 对所有接口生效
.allowedOrigins("http://localhost:8080") // 允许你的前端域名
.allowedMethods("GET", "POST", "OPTIONS") // 允许的请求方法(必须包含OPTIONS)
.allowedHeaders("Origin", "Content-Type", "Accept") // 允许的请求头(必须包含Content-Type)
.allowCredentials(true) // 是否允许带Cookie(可选,看需求)
.maxAge(3600); // 预检请求的缓存时间(减少 OPTIONS 请求次数)
}
}
这里要注意:allowedOrigins
别写(通配符),否则
allowCredentials
会失效(浏览器不允许通配符和Credentials共存);allowedMethods
必须包含OPTIONS
,否则预检请求会被拒绝。
第二步:写文件接收接口,处理细节才不会踩坑
文件接收用SpringBoot提供的MultipartFile
类,这是官方推荐的方式(Spring文档明确说过:“MultipartFile是处理文件上传的标准方式”)。接口写法很简单,但细节要注意:
@RestController
public class FileUploadController {
// 存储路径:比如D盘下的upload文件夹(记得创建文件夹)
private static final String UPLOAD_PATH = "D:/upload/";
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
//
判断文件是否为空
if (file.isEmpty()) {
return "文件为空,请重新选择";
}
//
处理文件名:加时间戳避免重复
String originalFileName = file.getOriginalFilename();
String fileName = System.currentTimeMillis() + "_" + originalFileName;
//
确保存储路径存在(比如D:/upload/202405/)
File dir = new File(UPLOAD_PATH + LocalDate.now().toString() + "/");
if (!dir.exists()) {
dir.mkdirs(); // 递归创建文件夹
}
//
保存文件到本地
try {
File dest = new File(dir.getAbsolutePath() + "/" + fileName);
file.transferTo(dest); // Spring推荐的保存方法,比自己读流更稳定
return "上传成功,文件路径:" + dest.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
return "上传失败:" + e.getMessage();
}
}
}
这里有几个“踩坑点”要提醒你:
System.currentTimeMillis()
)是最直接的解决办法,我帮老张就是这么做的。 application.properties
配置: properties
spring.servlet.multipart.max-file-size=10MB # 单个文件最大10MB
spring.servlet.multipart.max-request-size=10MB # 整个请求最大10MB
老张之前传2MB的图片就报错,找了半天才发现是这个配置的问题。
java
String suffix = originalFileName.substring(originalFileName.lastIndexOf(“.”));
if (!”.jpg”.equals(suffix) && !”.png”.equals(suffix)) {
return “只允许上传JPG/PNG图片”;
}
这能避免上传恶意文件(比如.exe),更安全。
我把前端后端的常见问题整理成了表格,你遇到问题直接对照找解决方法:
常见问题 | 问题原因 | 解决方法 |
---|---|---|
文件未传输到后端 | 前端FormData的key与后端参数名不一致;未设置processData和contentType为false | 确保key一致;Ajax选项添加processData: false, contentType: false |
跨域预检失败(OPTIONS请求报错) | 后端未允许OPTIONS方法或Content-Type头 | 全局CORS配置添加allowedMethods(“OPTIONS”)和allowedHeaders(“Content-Type”) |
大文件上传失败 | SpringBoot默认文件大小限制过小 | 修改application.properties中的max-file-size和max-request-size |
其实跨域传文件这件事,说难不难——前端把FormData和请求头配置对,后端把跨域和文件接收的细节做到位,基本不会出问题。老张搞定之后说:“原来这么简单,之前绕了好多弯路,早知道找你了。”
对了,还有个小技巧:遇到问题先看浏览器控制台的报错信息——跨域错误会显示“Access-Control-Allow-Origin missing”,文件没传过去会显示“file is empty”,顺着报错找原因准没错。
如果你按我讲的步骤做了,不管是成功还是遇到问题,都可以留言告诉我。比如你前端用的是Vue/React(其实Ajax逻辑一样,只是用axios代替jQuery),或者后端要传多个文件(FormData.append多个file就行),我都能帮你再细化~
其实现在很多前端同学都不用jQuery了,转而用axios,传文件的核心逻辑倒是和jQuery Ajax一样——不管用啥工具,只要涉及文件上传,都得用FormData把文件“包”起来,毕竟这是浏览器专门给文件传输设计的格式,绕不开的。你要是试过硬传文件不带FormData,大概率会收到后端的灵魂拷问:“你传的文件呢?咋是空的?”
不过用axios得注意两个小细节,不然容易踩坑。第一个是请求头配置——得明确告诉axios“我要传的是文件”,也就是在请求里加headers: {'Content-Type': 'multipart/form-data'}
。我之前犯过懒,觉得axios应该能自动识别FormData,结果没加这个头,请求发出去后后端拿到的MultipartFile
是空的,查了半小时才反应过来:axios默认的Content-Type
不是multipart/form-data
,得手动指定才行。
第二个是阻止axios乱改数据——axios有个默认行为,会把请求数据转换成它觉得“合适”的格式,比如把FormData转成JSON,但我们要的就是FormData本身啊!所以得加个transformRequest: [data => data]
,意思很直白:“你别瞎折腾我的数据,我给你啥你就原封不动发出去。”举个例子,完整的axios请求应该是这样的:axios.post('/upload', formData, { headers: {'Content-Type': 'multipart/form-data'}, transformRequest: [data => data] })
。
其实这两个配置和jQuery Ajax里的processData: false
、contentType: false
是一个逻辑,都是让工具库“别碰我的文件格式”——只不过axios的写法更“明确”一点而已。你要是记不住,就想:“FormData需要multipart/form-data
格式,而且不能被转换”,顺着这个思路配置准没错。
前端FormData的key和后端接口参数名不一致会有什么问题?
如果前端FormData的key(比如uploadFile)和后端@RequestParam指定的参数名(比如file)不一致,后端接收的MultipartFile对象会是空的,导致“没收到文件”的问题。解决方法很简单:将前端formData.append()的第一个参数改成和后端参数名完全一致即可。
SpringBoot跨域配置中allowedOrigins可以写通配符吗?
不 写通配符。因为如果你的请求需要携带Cookie(比如allowCredentials=true),浏览器会拒绝通配符与allowCredentials共存的配置,导致跨域失败。最好的做法是写具体的前端域名(比如http://localhost:8080),如果有多个前端域名,可以用逗号分隔添加。
前端用axios代替jQuery Ajax传文件,配置有什么不同?
核心逻辑一致,都是用FormData对象。axios需要注意两点配置:1)设置headers: {‘Content-Type’: ‘multipart/form-data’}(明确文件上传格式);2)添加transformRequest: [data => data](避免axios默认转换FormData为其他格式)。示例代码:axios.post(‘/upload’, formData, { headers: {‘Content-Type’: ‘multipart/form-data’}, transformRequest: [data => data] })。
如何用Ajax上传多个文件?
上传多个文件只需在前端循环遍历文件输入框的files数组,多次调用formData.append(‘file’, file)(key要和后端参数名一致);后端接口则用@RequestParam(“file”) List files接收,后续遍历列表处理每个文件即可(比如逐个保存到本地)。
修改SpringBoot的文件大小限制后还是上传失败,可能是什么原因?
常见原因有两个:1)配置项名称错误,正确的配置是spring.servlet.multipart.max-file-size(单个文件最大 size)和spring.servlet.multipart.max-request-size(整个请求最大 size),注意不要漏写或错写中间的.;2)单位格式错误,比如10MB要写成10MB(M大写),不能写成10mb或10m,否则SpringBoot会识别失败,仍使用默认的1MB限制。