SpringMVC入门笔记2

SpringMVC入门笔记2

1. SpringMVC的文件上传

1.1 文件上传的必要前提

  • form表单的enctype取值必须是:muitipart/form-data(默认值是:application/x-www-form-urlencoded),enctype:是表单请求正文的类型

  • 必须是POST请求

  • html中通过<input type="file" /> 来提交

  • commons-fileupload两个库


1.2 SpringMVC实现文件上传

  1. 添加maven依赖坐标
1
2
3
4
5
6
7
8
9
10
<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.6</version>
</dependency>

  1. springmvc.xml中配置文件解析器

需要注意的是,这里的id必须叫做multipartResolver,不能自己乱改

1
2
3
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760" /> <!--单位是字节-->
</bean>

  1. html

注意这里的

method=”post”

enctype=”multipart/form-data”

不能变

name=”upload”可以改,但是要和下面的controller层接收方法的参数名一致

1
2
3
4
<form action="user/fileUpload2" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload" /><br />
<input type="submit" value="上传">
</form>

  1. controller

注意这里的参数名upload要和html表单的文件的name属性名一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RequestMapping("/fileUpload2")
public String fileUpload2(HttpServletRequest request, MultipartFile upload) thro
//springmvc方式上传文件
//上传的位置(文件夹目录)
String path = request.getSession().getServletContext().getRealPath("/uploads
File file = new File(path);
//如果不存在,就创建
if (!file.exists()){
//创建文件夹
file.mkdirs();
}
//获取上传文件名称
String fileName = upload.getOriginalFilename();
//为了两次上传导致文件名相同而冲突,所以我们用uuid设置filename
String uuid = UUID.randomUUID().toString().replace("-", "");
// 完成文件上传
fileName = uuid + "_" + fileName;
upload.transferTo(new File(path, fileName));
return "success";
}

1.3 jersey跨服务器上传

在实际开发中,我们会有很多处理不同功能的服务器。例如:

  • 应用服务器:负责部署我们的应用

  • 数据库服务器:运行我们的数据库

  • 缓存和消息服务器:负责处理并发访问的缓存和消息

  • 文件服务器:负责存储用户上传文件的服务器。

1.3.1 步骤

  1. 新增pom坐标
1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.19.4</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.19.4</version>
</dependency>

  1. 创建新的module或者project作为图片服务器,并配置好tomcat(详情略),要注意在target中手动创建文件夹,tomcat两个端口不能冲突
只要添加目录和tomcat.png
  1. html同上
1
2
3
4
<form action="user/fileUpload3" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload" /><br />
<input type="submit" value="上传">
</form>

  1. controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RequestMapping("/fileUpload3")
public String fileUpload3(MultipartFile upload) throws Exception {
//跨服务器方式上传文件
//定义上传文件服务器路径
String path = "http://localhost:9090/uploads/";
//获取上传文件名称
String fileName = upload.getOriginalFilename();
//为了两次上传导致文件名相同而冲突,所以我们用uuid设置filename
String uuid = UUID.randomUUID().toString().replace("-", "");
fileName = uuid + "_" + fileName;
//创建客户端对象
Client client = Client.create();
//和图片服务器进行连接
WebResource webResources = client.resource(path + fileName);
//上传文件
webResources.put(upload.getBytes());
return "success";
}

1.3.2 常见错误的解决:

403错误

权限问题

打开tomcat安装目录apache-tomcat-8.5.46\conf\web.xml,ctrl+F搜索<servlet-name>default</servlet-name>找到如下图所示的位置,在如下图位置加入

1
2
3
4
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>

效果如下

添加后的图.png


404错误

目录问题,fileupload的tomcat配置改为根目录即可

tomcat中不要带虚拟目录.png

或者你不想改虚拟目录,那就要在应用服务器这边把上传的图片服务器的路径中加入刚刚的虚拟目录也是可以的

另一种方式,加入虚拟目录.png


405错误

同403错误


409错误

文件夹为空问题,因为上传图片的目录是uploads,所以手动在图片服务器的target目录创建uploads文件夹


500错误

原因复杂


2. SpringMVC异常处理

为了避免浏览器直接弹出错误

浏览器错误展示.png

让界面友好一点(比如页面提示系统正在维护中),我们可以通过异常处理器来跳转错误页面


springmvc.xml中

id只能是这个名字sysExceptionResolver,不能乱改

1
2
<!--配置异常处理器-->
<bean id="sysExceptionResolver" class="com.wjw.exception.SysExceptionResolver"/>

接下来我们创建并完成这两个类

新建两个类.png

自定义一个异常类SysException,继承Exception(像实体类那样的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SysException extends Exception {
//存储提示信息
private String message;

public SysException(String message) {
this.message = message;
}

@Override
public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}
}

SysExceptionResolver类

实现HandlerExceptionResolver接口,异常处理器

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
public class SysExceptionResolver implements HandlerExceptionResolver {

/**
* 处理异常的业务逻辑
* @param httpServletRequest
* @param httpServletResponse
* @param o
* @param e
* @return
*/
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
// 获取到异常对象
SysException ex = null;
if (e instanceof SysException){
ex = (SysException)e;
}else {
ex = new SysException("系统正在维护");
}
ModelAndView mv = new ModelAndView();
mv.addObject("errorMsg", e.getMessage());
mv.setViewName("error");
return mv;
}
}

使用方法如下

这样使用.png


3. SpringMVC拦截器

跟servlet中的过滤器filter一样,改名拦截器。有一丢丢不同,拦截器不会拦jsp,html,css,images等静态资源,但是过滤器如果配置了/*,全部都会拦下来。

3.1 xml方式

  1. springmvc.xml中
1
2
3
4
5
6
7
8
9
10
11
<!--配置拦截器-->
<mvc:interceptors>
<!--配置每个拦截器-->
<mvc:interceptor>
<!--拦截具体方法-->
<mvc:mapping path="/user/*"/>
<!--不拦截的方法-->
<!--<mvc:exclude-mapping path=""/>-->
<bean class="com.wjw.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
  • 可以使用多个<mvc:interceptor>标签来指定多个拦截器,拦截器的顺序就是配置顺序

  • mvc:mapping和mvc:exclude-mapping是一组相对的标签,设置拦截路径和不拦截路径


  1. 自定义拦截器类
  • 实现HandlerInterceptor接口
  • preHandle:预处理 controller方法前执行
  • postHandle:后处理 controller方法后,jsp/html执行之前
  • afterCompletion:最后处理 页面执行后执行的方法
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
public class MyInterceptor implements HandlerInterceptor {

/**
* 预处理 controller方法前执行
* return true为放行,执行下一个拦截器或controller方法
* return false则不放行,可以通过request和response来跳转
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器预处理运行了");
//request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request, response);
return true;
}

/**
* 后处理 controller方法后,jsp/html执行之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器后处理");
}

/**
* 页面执行后执行的方法
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion方法");
}
}

实现HandlerInterceptor接口需要实现三个方法,为了偷懒,可以

extends HandlerInterceptorAdapter

HandlerInterceptorAdapter是HandlerInterceptor接口的默认实现类,那么你需要哪个方法就overwrite哪个方法就行,其他写法上是一样的。


不使用xml的方式

spring当然也支持java配置类的方式,何况如果使用了springboot使用java配置类就更多了

  1. 拦截器还是跟上面一样实现HandlerInterceptor接口或者继承HandlerInterceptorAdapte类,但是要在类上方加上@Component注解

  1. xml不需要了,创建一个java配置类,实现WebMvcConfigurer接口,复写addInterceptors方法,具体代码如下:
1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class WjwMvcConfiguration implements WebMvcConfigurer {

@Autowired
private LoginInterceptor loginInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/**"); //拦截所有路径
}
}
#
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×