spring上传多个文件
① [FE] 用 FormData 上传多个文件到 MultipartFile[] 接口
最近有一个场景,在提交表单的时候,需要实现添加附件的功能,
表单内容要先提交到服务端,创建一个 issue,然后再将附件添加到这个 issue 中。
所以,附件在用户添加的时候,是 没有立即上传 的,
用户可以随意在浏览器端添加和删除,issue 创建后再一起上传。
前端采用的组件库是 antd ,用到了 upload 组件。
服务端接口是自定义实现的,也许并不支持 antd upload 上传组件的规范。
服务端接受数据时,使用了 MultipartFile ,这是 Spring 框架中常用的 写法 。
我们先看看 html input[type=file] 组件默认行为,
点击 “选择文件”,浏览器会弹出一个窗口,
选中一个文件,点 “打开”,就会触发 onchange 事件,
在 onchange 事件中,可以通过 e.target.files[0] 拿到刚才上传的那个 File 对象 。
再来看一下 upload 组件的默认行为,
点击 “添加”,浏览器也会弹出那个选择文件的窗口,
选中一个文件,点 “打开”,发现上传失败了。
打开控制台,看到 upload 组件向 / 这个地址发送了一个 POST 请求,
数据格式如下,
我们可以向 upload 组件传入 action 参数,修改 POST 请求地址,
但是,选中文件后立即上传 不符合 我们的场景,我们需要提交表单之后,将多个文件统一上传。
所以我们得自定义 upload 组件的行为。
upload 组件的有一个 customRequest 属性( #api ),
它可以配置自定义的上传行为。
我们的思路是,先将选中后自动上传的行为取消掉,然后再在提交表单后统一上传。
取消自动上传 的实现片段如下,
我们只需要在 customRequest 回调中,调用它的 onSuccess 参数即可。
删除也是可以用的,
现在我们添加两个附件,
接着来看前端怎样将这些附件,统一上传给服务端,具体实现如下,
可以看到请求成功了(项目中的 url 跟本例稍有不同,下图只为了示意),
还有几个需要注意的点:
上文 httpClient.post 实际调用了 XMLHttpRequest 发送请求,可能会遇到 跨域 的问题。
所以在调试上传接口的时候,需要检查一下服务端的配置,是否支持跨域请求。
CORS 相关的内容大致如下:
在预检请求阶段,服务端对 OPTIONS 请求的响应头中会包含 Access-Control-Allow-Origin ,
表明服务端接受该域 http://foo.example 的跨域请求。
注:
这里需要后端实现 OPTIONS 方法,后端框架一般会通过配置方式统一处理(返回 200 或 204,不能是 4xx)。
如果未配置统一处理方式,框架可能会直接返回 404 导致预检请求失败,CORS 请求也会失败。
使用 XMLHttpRequest 发送请求时,也可以携带 cookie 信息,
同时 预检请求中服务端响应头,也要包含 Access-Control-Allow-Credentials ,否则就不会发送 cookie
对于附带 cookie 的请求,服务器不能设置 Access-Control-Allow-Origin 的值为 “ * ”,否则请求将会失败。
而将 Access-Control-Allow-Origin 的值设置为具体的地址 http://foo.example ,请求才能成功。
我们上传功能用到了携带 cookie 的跨域请求,
可以看到服务端响应头中确实包含了, Access-Control-Allow-Credentials 和 Access-Control-Allow-Origin 两个字段。
Spring: Uploading Files
Spring: org.springframework.web.multipart #MultipartFile
ant-design v4.11.1
Ant Design - Upload #API
MDN: CORS
② 如何用SpringBoot框架来接收multipart/form-data文件
SpringBoot有它自己的接收请求的代码。下面就给大家详细介汪档绍一下它是如何实现单个文件和多个文件上传的功能的。
首选做一个简单的案例,也就是单个文件上传的案例。(这个案例是基于SpringBoot上面的,所以大家首先得搭建好SpringBoot这个框架)
前台HTML代码:
[html] view plain
<html>
<body>
<formaction="/upload"method="POST"enctype="multipart/form-data">
<inputtype="file"name="file"/>
<inputtype="submit"value="Upload"/>
</form>
</body>
</html>
/**
*文件上传具体实现方法;
*
*@paramfile
*@return
*/
@RequestMapping("/upload")
@ResponseBody
publicStringhandleFileUpload(@RequestParam("file")MultipartFilefile){
if(!file.isEmpty()){
try{
/*
*这段代码执行完毕之后,图片上传到了工程的跟路径;大家自己扩散下思维,如果我们想把图片上传到
*d:/files大家是否能实现呢?等等;
*这里只是简单一个例子,请自行参考,融入到实际中可能需要大家自己做一些思考,比如:1、文件路径;2、文件困粗乱名;
*3、文件格式;4、文件大小的限制;
*/
BufferedOutputStreamout=newBufferedOutputStream(
newFileOutputStream(newFile(
file.getOriginalFilename())));
System.out.println(file.getName());
out.write(file.getBytes());
out.flush();
out.close();
}catch(FileNotFoundExceptione){
e.printStackTrace();
return"上传失败,"+e.getMessage();
}catch(IOExceptione){
e.printStackTrace();
return"上传失败,"+e.getMessage();
}
return"上传成功";
}else{
return"上传失败,因为文件是空的.";
}
}
<!DOCTYPEhtml>
<htmlxmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>HelloWorld!</title>
</head>
<body>
<formmethod="POST"enctype="multipart/form-data"action="/batch/upload">
<p>文件1:<inputtype="text"name="id"/></p>
<p>文件2:<inputtype="text"name="name"/></p>
<p>文件3:<inputtype="file"name="file"/></p>
<p><inputtype="submit"value="上传"/></p>
</form>
</body>
</html>
@RequestMapping(value="/batch/upload",method=RequestMethod.POST)
@ResponseBody
publicStringhandleFileUpload(HttpServletRequestrequest){
=((MultipartHttpServletRequest)request);
List<凳隐MultipartFile>files=((MultipartHttpServletRequest)request)
.getFiles("file");
Stringname=params.getParameter("name");
System.out.println("name:"+name);
Stringid=params.getParameter("id");
System.out.println("id:"+id);
MultipartFilefile=null;
BufferedOutputStreamstream=null;
for(inti=0;i<files.size();++i){
file=files.get(i);
if(!file.isEmpty()){
try{
byte[]bytes=file.getBytes();
stream=newBufferedOutputStream(newFileOutputStream(
newFile(file.getOriginalFilename())));
stream.write(bytes);
stream.close();
}catch(Exceptione){
stream=null;
return"Youfailedtoupload"+i+"=>"
+e.getMessage();
}
}else{
return"Youfailedtoupload"+i
+"becausethefilewasempty.";
}
}
return"uploadsuccessful";
}
后台接收代码:
[java] view plain
这样就可以实现对multipart/form-data类型文件的接收了。那如果是多个文件外加多个字段呢,下面接着看下一个多个文件上传的案例。
前台HTML界面:
[html] view plain
后台接收代码:
[java] view plain
这样就可以实现对多个文件的接收了功能了。
③ springboot调整上传文件大小限制
Spring Boot文件上传,文件过大导致异常the request was rejected because its size (xxx) exceeds the configured maximum (xxx)
当进行文件操作时,如果文件体积过大,会抛出上述错误。解决方案有两种,一种是通过复写MultipartConfigElement;另一种是修改配置文件application.yml中multipart相关参数。
通过setMaxRequestSize限制限制上传的多个文件的总大小,setMaxFileSize限制单个文件的最大值。
properties配置:
参考:
https://blog.csdn.net/gnail_oug/article/details/80324120
https://ld246.com/article/1592018534503
https://blog.csdn.net/qq_27886773/article/details/102695145
④ spring mvc 多文件上传时报错MultipartHttpServletRequest
1、确认<form name="contractForm" id="contractForm" action="" method="post" enctype ="multipart/form-data">;
2、确认commons-fileupload.jar 和 org.springframework.web.jar 存在环境
3、MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request;
这一句会报错:ApplicationHttpRequest cannot be cast to MultipartHttpServletRequest
解决办法是通过这种方式:
MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
MultipartHttpServletRequest multipartRequest = resolver.resolveMultipart(request);