Spring 文件上传(一)

​ 这一节主要总结一下,ssm项目中配置CommonsMultipartResolver来完成前端文件上传的功能。

环境配置

**jar**包

CommonsMultipartResolver 主要用到了以下几个jar包:

  1. commons-fileupload:这是Apache Commons FileUpload库的jar包,提供了文件上传的功能,CommonsMultipartResolver使用了这个库来解析文件上传请求。
  2. commons-io:这是Apache Commons IO库的jar包,提供了一些IO操作的工具类,CommonsMultipartResolver可能会用到这些工具类来处理上传的文件数据。

这些jar包为CommonsMultipartResolver提供了文件上传所需的基本功能和支持,使得它能够有效地处理文件上传请求。

1
2
3
4
5
6
7
8
9
10
11
12
    <!--文件上传依赖-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.8.0</version>
</dependency>
</dependencies>

CommonsMultipartResolver主要做了什么

CommonsMultipartResolver 是 Spring 框架中用于处理文件上传的解析器。它的主要作用包括以下几点:

  1. 解析文件上传请求:CommonsMultipartResolver 用于解析客户端提交的包含文件上传的请求。它能够从请求中提取出上传的文件数据,并将其封装成 MultipartFile 对象,方便后续处理。
  2. 设置文件上传属性:通过配置 CommonsMultipartResolver,你可以设置一些文件上传的属性,比如最大上传文件大小、编码格式等。这些属性可以帮助你限制文件大小、指定编码方式等。
  3. 处理文件上传异常:CommonsMultipartResolver 还能够处理文件上传过程中可能出现的异常,比如文件大小超出限制等。它可以帮助你捕获这些异常并进行适当的处理。

总的来说,CommonsMultipartResolver 在 Spring MVC 中起到了解析和处理文件上传请求的作用,让你能够方便地处理客户端提交的文件数据。

配置CommonsMultipartResolver

1
2
3
4
5
<!--文件上传解析组件
id必须为multipartResolver
springmvc默认使用该id找该组件
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>

上传到本地

前端代码

​ 在ajax中,我们使用FormData封装数据,类似我们的form表单提交之后的效果。这样的话我们下面的Controller就可以正常接到了。要注意ajax需要带上的两个参数是processData:false, contentType:false

​ 在Ajax请求中, processDatacontentType 都是用来处理发送的数据的参数。

processData 是一个布尔值,用于指示 jQuery 是否应该转换发送的数据。如果设置为 false,则不会将数据转换为查询字符串格式。这在发送FormData对象或者其他不希望被转换的数据时非常有用。

contentType 是用来设置请求头中的 Content-Type 字段的值。它指定了发送数据的类型。当发送的数据是 FormData 对象或者文件时, contentType 应该被设置为 false,以便让浏览器自动识别并设置正确的 Content-Type

总的来说, processDatacontentType 都是用来控制发送数据的格式和类型,特别是在处理 FormData 对象和文件上传时非常有用。

processData 是一个布尔值,用于指示 jQuery 是否应该转换发送的数据。如果设置为 true,则会将数据转换为查询字符串格式。这意味着,如果你发送的是一个对象,它会被转换成类似 key1=value1&key2=value2 的形式,以便作为 URL 的一部分发送到服务器。这在发送简单的键值对数据时非常有用。对于POST请求,如果 processData设置为 true,数据将被转换为查询字符串格式并作为请求的正文内容发送到服务器。这通常用于将简单的键值对数据发送给服务器。但是,如果你发送的是 FormData 对象或者希望发送的数据不被转换,你应该将 processData设置为 false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<script src="./js/jquery.min.js"></script>
<script>
let showPic = ()=>{
let file = $("#file_input")[0].files[0];
let formdata = new FormData();
formdata.set("file",file)
$.ajax({
type:"post",
processData:false,
contentType:false,
url:"http://localhost:8080/file_upload_ssm_war_exploded/upload_file",
data: formdata,
success(resp){
console.log(resp);
}
});
};
</script>
<style type="text/css">
.imgFile{
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<h2>Hello World!</h2>

<form action="login">
用户名:<input name="username"><br>
密码:<input name="password"><br>
头像:<br>
<img class="imgFile" src="js:javascript(0)" ><br>
<input id="file_input" type="file" name="file" onchange="showPic()">
<button class="upload-button">上传图片</button><br>
</form>
</body>
</html>

后端代码

​ 以下代码将我们的文件存储到了本地项目upload文件下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@ResponseBody
@RequestMapping("/upload_file")
public Map<String,String> test(MultipartFile file,String abv,HttpServletRequest req) throws IOException {
String realPath = req.getServletContext().getRealPath("/upload");

File dir = new File(realPath);
System.out.println(abv);
if(!dir.exists()){
dir.mkdirs();
}
String fileName = UUID.randomUUID().toString();
String originalName = file.getOriginalFilename();
File file1 = new File(dir,fileName.concat(originalName.substring(originalName.lastIndexOf("."))));
file.transferTo(file1);
Map<String,String> map = new HashMap<>();
map.put("success","yes");
return map;
}

​ 为了解决我们的请求乱码,我们需要配置一个前端的字符过滤器,并且配置强制编码,不然配置会失效的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--Spring 中提供的字符编码过滤器-->
<filter>
<filter-name>encFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

存储结果

上传到远程的tomcat服务器

更改端口

首先我们需要了解,从tomcat配置文件中,我们可以看出,在启动tomcat的时候默认启动了3个端口,分别是8080(8443)、8009、8005。

8080(8443)端口

1
2
3
4
5
6
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"
maxParameterCount="1000"
/>

这个应该是我们最熟悉的一个,平常开发测试也经常用,该Connector用于监听浏览器发送的请求,设置为80后可以直接使用http://localhost访问。http协议,其中redirectPort表示如果发送的是https请求,就将请求发送到8443端口。8443是默认的https监听端口,默认是没有开启的,如果要开启由于tomcat不自带证书所以除了取消注释之外,还需要自己生成证书并指定。

8009端口

1
2
3
4
5
6
7
8
9
<!-- Define an AJP 1.3 Connector on port 8009 -->
<!--
<Connector protocol="AJP/1.3"
address="::1"
port="8009"
redirectPort="8443"
maxParameterCount="1000"
/>
-->

NginxApache等反向代理tomcat时就可以使用ajp协议反向代理到该端口。虽然我们经常使用http反向代理到8080端口,但由于ajp建立tcp链接后一般长时间保持,从而减少Http反复进行tcp链接和断开的开销,所以反向代理中ajp是比http高效的。

8005端口

1
<Server port="8005" shutdown="SHUTDOWN">

tomcat监听的关闭端口,就是说这个端口负责监听关闭tomcat的请求。当执行shutdown.sh关闭tomcat就是链接8005端口执行SHUTDOWN命令;由此,我们直接用telnet向8005端口执行SHUTDOWN来关闭tomcat,这也是比较正统的关闭方式,如果这个端口没被监听,那么sh脚本就是无效的。

从上面的介绍可以知道,我们开发的时候,tomcat使用的主要还是8080端口和8005端口,我们如果需要在开启一个图片服务器就需要更改这几个端口。

server.xml

更改只读属性

更改我们tomcatweb.xmlinitparam

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>

<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

创建对应上传目录

webapps下创建一个upload目录

导入依赖

这里我们使用了jersey-client的包,我们可以了解jersey-client客户端。这其实就是一个支持RestFullclient框架。

1
2
3
4
5
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.19</version>
</dependency>

前端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<script src="./js/jquery.min.js"></script>
<script>
let showPic = ()=> {
let file = $("#file_input")[0].files[0];
let formdata = new FormData();
formdata.set("file", file)
formdata.set("abv", "斯大林快回家")
//文件上传代码
$.ajax({
type: "post",
processData: false,
contentType: false,
url: "http://localhost:8080/file_upload_ssm_war_exploded/upload_file",
data: formdata,
success(resp) {
$(".imgFile").attr("src",resp.url);
console.log(resp.message);
}
});

};
</script>
<style type="text/css">
.imgFile{
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<h2>Hello World!</h2>

<form action="login">
用户名:<input name="username"><br>
密码:<input name="password"><br>
头像:<br>
<img class="imgFile" src="js:javascript(0)" ><br>
<input id="file_input" type="file" name="file" onchange="showPic()">

<button class="upload-button">上传图片</button><br>

<%-- <input type="submit" /><br>--%>

</form>
</body>
</html>

后端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.zgy.file.controller;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.zgy.file.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Controller
public class UploadController {
public String ServerUrl = "http://127.0.0.1:8090/upload/";
@ResponseBody
@RequestMapping("/upload_file")
public Map<String,String> test(MultipartFile file,String abv,HttpServletRequest req) throws IOException {
HashMap<String,String> map = new HashMap<>();
String originalName = file.getOriginalFilename();
String extendName = originalName.substring(originalName.lastIndexOf("."));
String newName = UUID.randomUUID().toString().concat(extendName);
Client client = Client.create();
WebResource resource = client.resource(ServerUrl + newName);
resource.put(String.class,file.getBytes());
map.put("message","success");
map.put("url",ServerUrl + newName);
return map;
}
}


效果图