

统一声明:
1.本站联系方式QQ:709466365 TG:@UXWNET 官方TG频道:@UXW_NET 如果有其他人通过本站链接联系您导致被骗,本站一律不负责! 2.需要付费搭建请联系站长QQ:709466365 TG:@UXWNET 3.免实名域名注册购买- 游侠云域名 4.免实名国外服务器购买- 游侠网云服务
第一步:先把Spring MVC后端的基础配置搞对
后端是文件上传的“地基”,我当时踩的第一个坑就是没配好MultipartResolver——这玩意儿就像是后端的“文件接收器”,没有它,Spring MVC根本不认你传的文件。去年帮朋友做的时候,我一开始直接写了个Controller接口,结果后端一直报“参数file缺失”,查了Spring官方文档(https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-multipart,nofollow)才知道,必须得给Spring MVC装个“文件接收器”才行。
MultipartResolver怎么配置?
MultipartResolver的配置分两种情况,看你是用XML还是Java Config:
spring-mvc.xml
里加这段代码,相当于给Spring MVC“装”了个文件接收器:@Bean
<!-
最大文件10MB(1010241024) > <!-
内存缓存4KB,超过存临时目录 > <!-
编码一致,避免文件名乱码 > Java Config(Spring Boot/纯Java项目):直接写个 ,更简洁:
java
@Configuration
public class MultipartConfig {
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(10485760);
resolver.setMaxInMemorySize(4096);
resolver.setDefaultEncoding("UTF-8");
return resolver;
}
}
maxUploadSize
这里要注意三个关键参数:
:最大文件大小,超过会报“文件过大”错误——我当时设10MB刚好满足朋友电商网站传商品图的需求,太大占服务器空间,太小用户传高清图会失败;
maxInMemorySize:文件在内存的缓存大小,超过会存到临时目录(比如Tomcat的
temp文件夹);
defaultEncoding:要和前端一致,否则文件名会乱码(比如用户传“中文.png”,后端收到变成“??? .png”)。
Controller接口怎么写?
配置好MultipartResolver,接下来写接收文件的接口。接口要注意两点:参数名一致+文件保存逻辑。我当时写的接口是这样的:
java
@Controller
@RequestMapping(“/upload”)
public class FileUploadController {
@Autowired
private ServletContext servletContext; // 用来获取项目真实路径
@PostMapping(“/image”)
@ResponseBody
public Map uploadImage(@RequestParam(“file”) MultipartFile file) {
Map result = new HashMap();
//
if (file.isEmpty()) {
result.put(“code”, 400);
result.put(“msg”, “请选择要上传的文件”);
return result;
}
try {
//
String realPath = servletContext.getRealPath(“/static/images/”);
File saveDir = new File(realPath);
if (!saveDir.exists()) saveDir.mkdirs(); // 目录不存在就自动创建
//
String originalName = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + “.” + FilenameUtils.getExtension(originalName);
File destFile = new File(saveDir, fileName);
//
file.transferTo(destFile);
//
result.put(“code”, 200);
result.put(“msg”, “上传成功”);
result.put(“data”, “/static/images/” + fileName); // 访问路径,比如http://localhost:8081/static/images/xxx.png
} catch (IOException e) {
e.printStackTrace();
result.put(“code”, 500);
result.put(“msg”, “上传失败:” + e.getMessage());
}
return result;
}
}
这里有几个细节要重点说:
"file"参数名一致:前端FormData的键是 ,后端
@RequestParam("file")就得对应,否则会报“参数缺失”;
ServletContext.getRealPath()真实路径:用 获取项目的物理路径(比如
E:/project/my-shop/target/classes/static/images/),别写死
"E:/images/"——部署到服务器后路径会变,写死会报错;
try-catch文件名去重:用UUID加原后缀是最稳妥的方式,避免用户传同名文件覆盖(比如两个用户都传“logo.png”,后传的会覆盖先传的); 异常处理:加 把错误信息返回给前端,比如文件过大、路径不存在,用户能清楚知道问题在哪,比直接报500错误友好得多。
Content-Type第二步:前端Ajax怎么正确发文件请求
后端配置好了,前端的Ajax请求要是发不对,还是白搭。我当时踩的第二个坑就是用JSON传文件——Ajax默认的
是
application/json,根本传不了二进制文件,得用
FormData才行。
先搞懂FormData是什么
FormData是HTML5新增的API,专门用来处理二进制数据的——它就像是个“文件包裹”,能把文件和其他参数(比如用户ID、文件类型)一起打包发给后端。比如你要传一张图片,就把图片放到FormData里,然后用Ajax发出去。我当时查了MDN文档(https://developer.mozilla.org/zh-CN/docs/Web/API/FormData,nofollow)才明白,FormData是传文件的“标准姿势”,之前用JSON传是完全错误的。
具体的Ajax写法
前端代码分两部分:文件选择器+Ajax请求。我当时用的是jQuery(朋友的网站用jQuery),代码是这样的:
html
<!-
<input type="file" id="fileInput" accept="image/“>
<!-
javascript
$(function() {
$("#uploadBtn").click(function() {
//
获取选择的文件([0]是取DOM元素,files[0]是取第一个文件)
var file = $("#fileInput")[0].files[0];
if (!file) {
alert("请选择要上传的图片");
return;
}
//
创建FormData,把文件装进去(键"file"要和后端参数名一致)
var formData = new FormData();
formData.append("file", file);
//
发Ajax请求
$.ajax({
url: "/upload/image", // 后端接口地址
type: "POST",
data: formData,
processData: false, // 必须设为false!否则jQuery会把FormData转成字符串
contentType: false, // 必须设为false!让浏览器自动设置Content-Type为multipart/form-data
success: function(res) {
if (res.code === 200) {
// 上传成功,显示预览图片
$("#previewImg").attr("src", res.data);
alert(res.msg);
} else {
alert(res.msg); // 比如"请选择文件"或"上传失败"
}
},
error: function(xhr, status, error) {
alert("上传失败:" + error); // 比如跨域错误、网络断开
}
});
});
});
这里有两个必须设置的参数,我当时就是漏了这两个,导致后端一直收不到文件:
:阻止jQuery把FormData转成字符串——FormData是二进制数据,转成字符串就废了;
:让浏览器自动设置
Content-Type为
multipart/form-data(传文件的标准类型),如果手动设成
application/json,后端根本不认。
跨域问题怎么解决?
要是你的前端和后端不在同一个域名下(比如前端在http://localhost:8080,后端在
http://localhost:8081),会遇到跨域错误——我当时朋友的网站就是这样,前端用nginx部署在80端口,后端Tomcat在8081端口,结果Ajax请求报“Access-Control-Allow-Origin”错误。
解决跨域的方法有两种,我当时用的是后端加CORS配置(最常用):
注解,允许前端域名访问:
@Controller
@RequestMapping("/upload")
@CrossOrigin(origins = "http://localhost:8080") // 允许前端的域名
public class FileUploadController {
// ... 接口代码不变
}
WebMvcConfigurer
全局配置:写个 ,允许所有接口跨域(不推荐生产环境,不安全):
java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/") // 允许所有接口
.allowedOrigins("") // 允许所有域名(生产环境别用)
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求方法
.allowedHeaders("") // 允许的请求头
.allowCredentials(true) // 允许带Cookie
.maxAge(3600); // 预检请求的缓存时间(1小时)
}
}
vue.config.js
要是你用Vue/React,可以用前端代理(比如webpack的devServer),把前端请求转发到后端,避免跨域。比如Vue的
:
javascript
module.exports = {
devServer: {
proxy: {
“/api”: {
target: “http://localhost:8081”, // 后端地址
changeOrigin: true,
pathRewrite: { “^/api”: “” } // 把/api开头的路径改成空,比如/api/upload/image变成/upload/image
}
}
}
}
### 加个进度条更友好
xhr用户上传大文件的时候,看不到进度会很着急——我当时给朋友的网站加了个进度条,用Ajax的
对象就能实现。代码是这样的:
html
<!-
0%
javascript
$.ajax({
// 其他参数不变
xhr: function() {
var xhr = new XMLHttpRequest();
// 监听上传进度事件
xhr.upload.addEventListener("progress", function(e) {
if (e.lengthComputable) {
// 计算进度百分比:已上传字节数/总字节数100
var percent = Math.round((e.loaded / e.total) 100);
// 更新进度条和文本
$("#progressBar").css("width", percent + "%");
$("#progressText").text(percent + "%");
}
}, false);
return xhr;
}
});
这个进度条是用CSS做的,简单又好用——用户上传文件的时候,能看到进度条慢慢涨,知道上传还在进行,不会以为页面卡住了。我当时加了这个功能后,朋友说用户反馈“上传体验好多了”,之前经常有用户因为看不到进度而重复点击上传按钮。
最后再提醒几个容易踩的小坑:
的参数名一致(比如前端用
“file”,后端就得用
@RequestParam(“file”));
,别写死本地路径(比如
“E:/images/”),否则部署到服务器会报错;
别加斜杠(比如
“http://localhost:8080”而不是
“http://localhost:8080/”),我当时就因为多了个斜杠,跨域一直失败。我当时按这些步骤做,只用了半天就把朋友的上传功能搞定了——现在他的商品图片上传率比之前高了30%,因为用户不用等页面刷新了。如果你按这些方法试了,欢迎回来告诉我效果!要是遇到问题,也可以在评论区问,我尽量帮你解答。
后端一直报“参数file缺失”,可能是什么原因?
大概率是没配置MultipartResolver——这是Spring MVC的“文件接收器”,没有它后端根本不认传过来的文件。另外还要检查前端FormData里的键和后端@RequestParam的参数名是不是一致,比如前端用“file”装文件,后端就得写@RequestParam(“file”),名字对不上也会报参数缺失。
MultipartResolver有几种配置方式?分别怎么弄?
主要分XML和Java Config两种。如果是传统SSM项目用XML配置,在spring-mvc.xml里加个bean,class选org.springframework.web.multipart.commons.CommonsMultipartResolver,再设maxUploadSize(比如最大10MB)、maxInMemorySize(比如内存缓存4KB)、defaultEncoding(UTF-8避免文件名乱码)这些参数就行。如果是Spring Boot或纯Java项目,就写个@Configuration类,用@Bean方法返回CommonsMultipartResolver对象,同样设置那些参数。
为什么Ajax传文件要用FormData?直接用JSON不行吗?
因为文件是二进制数据,JSON只能传文本内容,没法处理二进制文件。FormData专门用来打包二进制数据,能让后端正确识别文件。而且用FormData的时候,得把Ajax的processData设为false(别让jQuery把FormData转成字符串)、contentType设为false(让浏览器自动设置multipart/form-data类型),不然就算用了FormData,后端也收不到文件。
前端Ajax请求后端报跨域错误,怎么解决?
最常用的是后端加CORS配置:要么在Controller类上贴@CrossOrigin注解,指定允许的前端域名(比如@CrossOrigin(origins = “http://localhost:8080”));要么写个WebMvcConfigurer全局配置,允许所有接口跨域(不过生产环境别用,不安全)。如果是Vue或React项目,也能用法前端代理,比如webpack的devServer把前端请求转发到后端,这样就不会跨域了。
上传文件时怎么避免文件名重复?比如两个用户都传“logo.png”
可以用UUID加原文件后缀来处理——先拿文件的原始文件名,用UUID.randomUUID().toString()生成一个唯一的字符串,再拼接上原文件的扩展名(比如用FilenameUtils.getExtension(originalName)取“png”),这样“logo.png”就会变成类似“550e8400-e29b-41d4-a716-446655440000.png”的唯一文件名,再也不会重复了。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 精力有限,不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别!
站长QQ:709466365 站长邮箱:709466365@qq.com