1. SpringMVC中静态资源拦截问题
# springmvc中静态资源拦截问题
1. 出现静态资源拦截问题的原因
注意:由于在web.xml中配置springmvc的核心servlet DispatcherServlet 时url-pattern 配置为“/”,
因此会导致项目中所有 / 开头请求,均被视为控制器请求处理,这样会导致项目中的静态资源(css,js,imp)
被拦截
解决方案:
1. url-pattern / 导致静态资源拦截
*.action *.do *.haha (配置成什么后缀需要加什么访问) [不推荐]
使用这种方式日后访问路径结尾必须加入指定后缀 url.do如下
http://localhost:9999/ssm_springmvc/user/findAll.do
2. url-pattern 依然使用 /
在springmvc配置文件中加入如下配置
<mvc:default-servlet-handler/> [推荐]
在findAll.jsp文件引入静态资源jquery-3.5.1.min.js
<!--在页面加载完成之后触发alert-->
<!--引入js,pageContext.request.contextPath表示项目名-->
<script src="${pageContext.request.contextPath}/js/jquery-3.5.1.min.js"></script>
<script>
$(function(){
alert(); <!--页面加载完成之后触发alert-->
})
</script>
我们来查一下错:
首先在页面点一下右键,然后点击检查,会出现以下页面:
之后按照下图所示操作:
我们发现上面的路径是对的,然后我们想是不是没有部署过去,我们接着往下看:
之后我们到target下去查看
可以看到js包,说明我们没有部署过来,那这个怎么办呢,target里面有可能出现缓存,我们clean一下
clean之后我们再重新启动,然后看看这次有没有
然后这次我们再次访问findAll,看看能不能出现alert
我们看到还是报404错误,那为什么?
其实静态资源的请求都被springmvc拦截掉了,之所以会出现这个问题,是因为在现有的web.xml中配置的拦截请求,拦截的是/,
<!--web.xml配置文件中的-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
咱们去访问静态资源/js/jquery-3.5.1.min.js,这也相当于服务器端的一个请求,这个请求也会被springmvc拦截到,拦截到之后会把这个请求当成控制器去处理,它就会去找有没有一个namespace是js,方法的名字是jquery-3.5.1.min.js这样的一个方法,它把它当成是一个控制器的请求了,所以它肯定找不到对应的控制器,找不到就会报404。
<!--findAll.jsp文件中的-->
<!--引入js,pageContext.request.contextPath表示项目名-->
<script src="${pageContext.request.contextPath}/js/jquery-3.5.1.min.js"></script>
因为咱们是以/拦截的,它会把所有/的请求都作为控制器请求,找的时候肯定找不到,而这里我们希望把它当成静态资源去找。
解决方案
- 方案一
我们在发送请求时可以拦截以.do结尾的,在web.xml文件中进行配置
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
虽然可以成功解决静态资源拦截问题,这种方式我们以后每次去访问都要加一个后缀,但是官方还是建议我们在访问时什么都不加,没有后缀,也就是说这种方式其实官方并不建议,官方还是建议我们在web.xml文件中配置成/这种的,一配置成/什么都拦,所以它也给我们提供了第二种解决方案。
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 方案二
在web.xml文件中仍然使用 / 拦截所有请求,但是在springmvc.xml配置文件中加入如下配置
<!--解决静态资源拦截的配置,加上这个配置会沿用默认的Servlet处理机制-->
<mvc:default-servlet-handler/>
默认的servlet处理机制指的是什么意思呢:也是拦截所有请求,但是在拦截到所有请求之后并不是把所有请求上来就作为控制器去处理,而是以 .js .css .png .img 这些后缀结尾的请求,先去找它对应的静态资源,找到的话直接响应,找不到的话再作为控制器去处理,如果两次都找不到会报404,有可能是路径错了或者是名字错了。
总结:
2. SpringMVC中文件上传
2.1 文件上传的流程
# springmvc中文件上传
1. 定义
指的是用户将自己本地计算机中文件通过网络的形式上传到系统所在服务器上的过程,这个过程称之为文件上传
2. 文件上传功能不是所有系统都需要的
什么时候需要文件上传功能: 需要用户提交文件时 必须开发文件上传功能
3. springmvc中如何开发文件上传
1). 在系统中开发一个可以进行文件上传页面 包含一个form表单 在表单中开发一个可以选择本地计算机文件入口
<form>
<input type="file" name=""/>
<input type="submit" value="提交"/>
</form>
2). form表单 method
a. form method提交方式必须是post (不可以用get,get是走地址栏,它有长度限制,用户文件大了就传不了了)
b. enctype application/x-www-form-urlencoded(文本)| multipart/form-data(二进制)
对表单中表单控件做编码,默认:application/x-www-form-urlencoded 对文本数据做编码用的
3). 开发Controller 在控制器方法中使用MultipartFile 进行文件的接收
4). 在springmvc配置文件中加入文件上传解析器配置
CommonsMultipartResolver
<bean class="xxxx.xxx.xx.CommonsMultipartResolver" id="multipartResolver">
要求:文件上传解析器必须存在id 且 id必须为multipartResolver
文件上传解析器作用:解析文件上传时对文件数据的处理,完成二进制文件和对象之间的转换
5). 引入文件上传的相关依赖
commons-fileupload
步骤3:
接下来我们来实现一下文件上传功能
首先搭建环境:
在pom.xml文件中引入springmvc相关依赖(先不考虑ssm整合)和commons-fileupload:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<!--springmvc核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<!--servlet-api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!--jstl-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--commons-fileupload-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
配置一下web.xml配置文件:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置post请求方式中文乱码的Filter-->
<filter>
<filter-name>charset</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>
</filter>
<filter-mapping>
<filter-name>charset</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置springmvc的核心servlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置springmvc配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern> <!-- / 代表拦截所有请求,交给springmvc处理-->
</servlet-mapping>
</web-app>
配置一下springmvc.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--开启注解扫描-->
<context:component-scan base-package="com.baizhi.controller"/>
<!--开启注解驱动-->
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
<!--配置静态资源拦截-->
<mvc:default-servlet-handler/>
<!---配置文件上传解析器
id:必须指定为 multipartResolver
-->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver"/>
</beans>
注:在配置环境时已经将第4、5步做完了,剩下的就是开发表单,开发控制器了
- 开发表单
在webapp包下创建upload.jsp
<%@page contentType="text/html; UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>测试文件上传</title>
</head>
<body>
<h1>文件上传</h1>
<form action="${pageContext.request.contextPath}/file/upload" method="post" enctype="multipart/form-data">
<input type="file" name="img"/>
<input type="submit" value="上传文件">
</form>
</body>
</html>
- 开发控制器
/**
* 用来测试文件上传下载
*/
@Controller
@RequestMapping("file")
public class FileController {
// 用来处理文件上传
@RequestMapping("upload")
public String upload(MultipartFile img, HttpServletRequest request) throws IOException {
System.out.println("文件名:" + img.getOriginalFilename());
System.out.println("文件大小" + img.getSize());
System.out.println("文件类型" + img.getContentType());
// 文件上传
// 1. 根据upload相对路径获取部署到服务器之后绝对路径
String realPath = request.getSession().getServletContext().getRealPath("/upload");;
// 2. 将文件上传到upload对应的路径中
img.transferTo(new File(realPath, img.getOriginalFilename()));
return "index";
}
}
注意:当upload这个包中没有任何文件时,我们开启服务器服务器上是没有upload包的(这是idea的原因,包中没有文件不会部署),为了可以部署到服务器,我们可以在upload包下随便创建一个文件
我们在upload包下随便创建一个文件以使得upload包可以部署在服务器上
测试
上传之后:
从上图我们可以看到上传成功了
总结
2.2 文件上传的细节
# 文件上传细节处理
1. 如何修改文件上传时文件的原始名称问题
img.getOriginalFilename() ===> 获取的是原始文件名
问题:直接使用文件的原始名称作为上传文件的最终名称,会导致重名文件覆盖的问题
解决:每次用户上传文件修改一个新的名称 (但是后缀不能变,比如txt、png)
新文件名: UUID+文件后缀
// 修改文件原始名称
String extension = FilenameUtils.getExtension(img.getOriginalFilename()); // 拿到文件后缀
String newFileName = UUID.randomUUID().toString() + "." + extension;
// UUID生成的文件名中间都有一个 - 如果不想要这个- 可以使用下面语句去除
//String newFileName = UUID.randomUUID().toString().replace("-", "") + "." + extension;
2. 便于上传的文件进行管理需要将用户每天上传的文件放入当天日期目录中
// 生成当天日期目录
LocalDate now = LocalDate.now();
File dateDir = new File(realPath, now.toString());//父级目录是realPath,子级目录是now(自己是now)
if(!dateDir.exists()) dateDir.mkdirs(); // 如果不存在,那就创建;存在不做任何操作
3. springmvc中如何解决文件上传的大小限制
注意:在springmvc中上传文件默认没有大小限制 在struts2中默认上传文件最大不能超过2M
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<!--注入文件上传下载大小限制 单位:字节 2M=2097152字节 默认:没有限制-->
<property name="maxUploadSize" value="2097152"/>
</bean>
- 如何修改文件上传时文件的原始名称
因为可能多个用户上传了相同名称的文件,比如自我介绍,都叫自我介绍.word,这样上传到服务器上会由于同名问题造成文件的覆盖,
为了解决这种问题,我们需要修改文件上传时的原始名称。
我们在修改文件时不可以修改文件的后缀,比如 .txt .png 等等,所以我们首先需要获得文件名的后缀,那么获取文件名的后缀有几种方式呢?
// 获取文件后缀名
public static void main(String[] args) {
String fileName = "aa.txt";
// 1. 分隔符 // 因为 . 小数点是特殊字符,需要转移为普通的字符
String[] spilt = fileName.split("\\."); // \表示转义,在字符串中需要使用\\将第二个\转移为转义符然后转义.
System.out.println(spilt[spilt.length - 1]);
// 2. 字符串subString
int i = fileName.lastIndexOf(".");
String substring = fileName.substring(i);
System.out.println(substring);
// 3. 工具类型 commons-fileupload工具类
String extension = FilenameUtils.getExtension(fileName); // 获取扩展名
System.out.println(extension);
}
String fileName = "aa.txt";
有三种方法,分别是:
-
分隔符
-
以 . 小数点进行分隔,获取最后一个字符串即为文件后缀(最后一个字符串不含小数点)
-
// 1. 分隔符 // 因为 . 小数点是特殊字符,需要转移为普通的字符 String[] spilt = fileName.split("\\."); // \表示转义,在字符串中需要使用\\将第二个\转移为转义符然后转义. System.out.println(spilt[spilt.length - 1]);
-
-
字符串subString
-
获得 . 小数点的位置,然后用subString截取从这个小数点位置到结尾的字符串(截取的字符串含 . 小数点)
-
// 2. 字符串subString int i = fileName.lastIndexOf("."); String substring = fileName.substring(i); System.out.println(substring);
-
-
工具类 [推荐,特别方便]
-
直接调用工具类的方法获得扩展名(后缀名),获取的后缀名没有 . 小数点
-
// 3. 工具类型 commons-fileupload工具类 String extension = FilenameUtils.getExtension(fileName); // 获取扩展名 System.out.println(extension);
-
获取到了文件的后缀名,那我们怎么修改文件名字呢,修改为什么好呢,我们一般使用UUID进行文件名字的命名,因为UUID是通用唯一识别码,不会有重复,接下来我们来看看如何修改文件名字。
@Controller
@RequestMapping("file")
public class FileController {
// 用来处理文件上传
@RequestMapping("upload")
public String upload(MultipartFile img, HttpServletRequest request) throws IOException {
// 文件上传
// 1. 根据upload相对路径获取部署到服务器之后绝对路径
String realPath = request.getSession().getServletContext().getRealPath("/upload");
// 2. 修改文件原始名称
String extension = FilenameUtils.getExtension(img.getOriginalFilename()); // 拿到文件后缀
String newFileName = UUID.randomUUID().toString().replace("-", "") + "." + extension;
// UUID生成的文件名中间都有一个 - 如果不想要这个- 可以使用下面语句去除
//String newFileName = UUID.randomUUID().toString().replace("-", "") + "." + extension;
// 3. 将文件上传到upload对应的路径中
img.transferTo(new File(realPath, newFileName));
return "index";
}
}
启动之后:
- 如何将用户当天上传的文件放入当天的日期目录中
为了方便管理,有时候我们希望用户把当天上传的文件上传到对应的当天文件目录中,那么我们该怎么做呢。
@Controller
@RequestMapping("file")
public class FileController {
// 用来处理文件上传
@RequestMapping("upload")
public String upload(MultipartFile img, HttpServletRequest request) throws IOException {
System.out.println("文件名:" + img.getOriginalFilename());
System.out.println("文件大小" + img.getSize());
System.out.println("文件类型" + img.getContentType());
// 文件上传
// 1. 根据upload相对路径获取部署到服务器之后绝对路径
String realPath = request.getSession().getServletContext().getRealPath("/upload");
// 2. 修改文件原始名称
String extension = FilenameUtils.getExtension(img.getOriginalFilename()); // 拿到文件后缀
String newFileName = UUID.randomUUID().toString() + "." + extension;
// UUID生成的文件名中间都有一个 - 如果不想要这个- 可以使用下面语句去除
//String newFileName = UUID.randomUUID().toString().replace("-", "") + "." + extension;
// 3. 生成当天日期目录
LocalDate now = LocalDate.now(); // 获取当天日期时间,等价于(有一点区别) new Date,LocalDate不存在时、分、 // 秒,new Date不存在时、分、秒
File dateDir = new File(realPath, now.toString());//父级目录是realPath,子级目录是now(自己是now)
if(!dateDir.exists()) dateDir.mkdirs(); // 如果不存在,那就创建;存在不做任何操作
// 4. 将文件上传到upload对应日期的目录中
img.transferTo(new File(dateDir, newFileName));
return "index";
}
}
上传测试:
补充一下,虽然 new Date() 和 LocalDate.now()都可以获取到当天日期,但是 Date 还含有时、分、秒,不是格式化,获取格式化还要调用好几个api;LocalDate不含时、分、秒,是格式化的形式 yyyy-MM-dd,由于LocalDate获取格式化的方法比Date简单许多,所以更推荐使用 LocalDate
- springmvc如何解决文件上传的大小限制
在springmvc.xml文件中配置文件解析器CommonsMultipartResolver的属性
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<!--注入文件上传下载大小限制 单位:字节 2M=2097152字节 默认:没有限制-->
<property name="maxUploadSize" value="2097152"/>
</bean>
总结