游侠网云服务,免实名免备案服务器 游侠云域名,免实名免备案域名

统一声明:

1.本站联系方式
QQ:709466365
TG:@UXWNET
官方TG频道:@UXW_NET
如果有其他人通过本站链接联系您导致被骗,本站一律不负责!

2.需要付费搭建请联系站长QQ:709466365 TG:@UXWNET
3.免实名域名注册购买- 游侠云域名
4.免实名国外服务器购买- 游侠网云服务
手把手教你用Ajax+Spring MVC实现文件上传:详细步骤+完整代码 一

第一步:先把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:

  • XML配置(传统SSM项目):在spring-mvc.xml里加这段代码,相当于给Spring MVC“装”了个文件接收器:
  • 
    

    <!-

  • 最大文件10MB(1010241024) >
  • <!-

  • 内存缓存4KB,超过存临时目录 >
  • <!-

  • 编码一致,避免文件名乱码 >
  • Java Config(Spring Boot/纯Java项目):直接写个
  • @Bean,更简洁: 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(); // 目录不存在就自动创建

    //

  • 处理文件名:用UUID避免重复(比如用户传”logo.png”,变成”550e8400-e29b-41d4-a716-446655440000.png”)
  • 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;

    }

    }

    这里有几个细节要重点说:
    
  • 参数名一致:前端FormData的键是
  • "file",后端@RequestParam("file")就得对应,否则会报“参数缺失”;
  • 真实路径:用
  • ServletContext.getRealPath()获取项目的物理路径(比如E:/project/my-shop/target/classes/static/images/),别写死"E:/images/"——部署到服务器后路径会变,写死会报错;
  • 文件名去重:用UUID加原后缀是最稳妥的方式,避免用户传同名文件覆盖(比如两个用户都传“logo.png”,后传的会覆盖先传的);
  • 异常处理:加
  • try-catch把错误信息返回给前端,比如文件过大、路径不存在,用户能清楚知道问题在哪,比直接报500错误友好得多。

    第二步:前端Ajax怎么正确发文件请求

    后端配置好了,前端的Ajax请求要是发不对,还是白搭。我当时踩的第二个坑就是用JSON传文件——Ajax默认的

    Content-Typeapplication/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/“>

    <!-

  • 图片预览区域 >
  • 手把手教你用Ajax+Spring MVC实现文件上传:详细步骤+完整代码 二

    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); // 比如跨域错误、网络断开

    }

    });

    });

    });

    这里有两个必须设置的参数,我当时就是漏了这两个,导致后端一直收不到文件:

  • processData: false:阻止jQuery把FormData转成字符串——FormData是二进制数据,转成字符串就废了;
  • contentType: false:让浏览器自动设置Content-Typemultipart/form-data(传文件的标准类型),如果手动设成application/json,后端根本不认。
  • 跨域问题怎么解决?

    要是你的前端和后端不在同一个域名下(比如前端在http://localhost:8080,后端在http://localhost:8081),会遇到跨域错误——我当时朋友的网站就是这样,前端用nginx部署在80端口,后端Tomcat在8081端口,结果Ajax请求报“Access-Control-Allow-Origin”错误。

    解决跨域的方法有两种,我当时用的是后端加CORS配置(最常用):

  • 局部配置:在Controller上加@CrossOrigin注解,允许前端域名访问:
  •  @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/React,可以用前端代理(比如webpack的devServer),把前端请求转发到后端,避免跨域。比如Vue的

    vue.config.js

    javascript

    module.exports = {

    devServer: {

    proxy: {

    “/api”: {

    target: “http://localhost:8081”, // 后端地址

    changeOrigin: true,

    pathRewrite: { “^/api”: “” } // 把/api开头的路径改成空,比如/api/upload/image变成/upload/image

    }

    }

    }

    }

    ### 加个进度条更友好
    

    用户上传大文件的时候,看不到进度会很着急——我当时给朋友的网站加了个进度条,用Ajax的

    xhr对象就能实现。代码是这样的:

    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做的,简单又好用——用户上传文件的时候,能看到进度条慢慢涨,知道上传还在进行,不会以为页面卡住了。我当时加了这个功能后,朋友说用户反馈“上传体验好多了”,之前经常有用户因为看不到进度而重复点击上传按钮。

    最后再提醒几个容易踩的小坑:

  • 前端FormData的键要和后端@RequestParam的参数名一致(比如前端用“file”,后端就得用@RequestParam(“file”));
  • 后端的保存路径要用ServletContext.getRealPath(),别写死本地路径(比如“E:/images/”),否则部署到服务器会报错;
  • 跨域配置的时候,allowedOrigins别加斜杠(比如“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”的唯一文件名,再也不会重复了。