SpringMVC+Spring+Mybatis整合应用(3)

1. 实现图片上传

需求:在用户注册页面实现上传图片做为用户头像javascript

    1. springmvc中对多部件类型请求数据解析:在页面form中提交enctype="multipart/form-data"的数据时,须要springmvc对multipart类型的数据进行解析。在springmvc.xml中配置multipart类型解析器。html

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--指定上传文件的最大空间为10mb-->
		<property name="maxUploadSize">
			<value>1048576</value>
		</property>
<!--上传文件的编码格式为utf-8-->
		<property name="defaultEncoding">
			<value>utf-8</value>
		</property>
	</bean>

    2. 加入上传图片的jar,上边的解析内部使用下边的jar进行图片上传。前端

    3. 在tomcat服务器中建立图片虚拟目录用于存储图片:经过图形化界面建立,java

Document base就表示本地路径,而path就表示浏览器访问路径;也能够直接修改tomcat的配置:在conf/server.xml文件,添加虚拟目录 :jquery

注意:在图片虚拟目录 中,必定将图片目录分级建立(提升i/o性能),通常咱们采用按日期(年、月、日)进行分级建立。web

    3. jsp页面中对上传图片代码编写:主要有三个要编写的地方,form表单的enctype="multipart/form-data"和method="post"两个属性,以及<input type="file" name="">标签上传文件ajax

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>注册页面</title>
    
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
  </head>
  <body>
<!--enctype="multipart/form-data"和method="post"这两个配置必须有,multipart/form-data表示上传多部件类型的数据,必须为post请求方式是由于post请求方式能发送包含文件的数据请求-->
    <form action="/user_manage/user/regist.do" enctype="multipart/form-data" method="post">
	   	<div>请输入名称:<input type="text" name="userName"></div>
    	<div>请输入密码:<input type="password" name="userPassword"></div>
    	<div>请输入年龄:<input type="text" name="userAge"></div>
    	<div>请输入地址:<input type="text" name="userAddress"></div>
    	<div>请输入手机:<input type="text" name="userTelephone"></div>
<!--主要经过<input type="file" name="">标签上传文件,不必定是图片-->
    	<div>请选择一张图片做为头像:<input type="file" name="img"></div>
    	<div><input type="submit" value="注册"></div>
    </form>
  </body>
</html>

    4. 服务端controller方法接收图片文件:接收的文件会绑定为org.springframework.web.multipart.MultipartFile类型的对象形参,也就是说,发送的文件会保存在MultipartFile对象中,绑定规则和简单类型参数的绑定同样,形参名和请求中的key值相同spring

@RequestMapping("/regist")
	public String userRegist(MultipartFile img,@Validated(value={validatorGroup1.class}) User user,BindingResult result,Model model){
		String picPath=uploadPicture(img);//调用下面的方法,保存图片并返回该图片的访问地址
		user.setUserImage(picPath);//将图片的访问地址保存到User对象中
		userservice.insertUser(user);
		model.addAttribute("userAccount", user.getUserAccount());
		return "login";
	}

public class User {
    private Integer userAccount;
    private String userName;
    private String userPassword;
    private Integer userAge;
    private String userAddress;
    private String userTelephone;
//添加图片路径属性
    private String userImage;
//省略get/set方法
}

    5. 将接收的文件发送到图片服务器的虚拟目录中:json

//保存图片到服务器中,并生成该图片的访问路径
private static String uploadPicture(MultipartFile uploadFile){
		String oldFileName=uploadFile.getOriginalFilename();//获取初始文件名
		String fileSuffix=oldFileName.substring(oldFileName.lastIndexOf("."));//获取文件类型后缀
		String newFileName=UUID.randomUUID().toString();//新图片名称的生成有多种方式,只需保证不重复便可

		//本地服务器中存放图片所在文件夹的物理路径
		String path="D:\\develop\\upload\\";
        //拼接本地存放的完整路径
		File newPicture=new File(path+newFileName+fileSuffix);
		try {
            //将图片保存在该路径下
			uploadFile.transferTo(newPicture);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
        //返回完整的虚拟路径
		return "http://localhost:8081/pic/"+newFileName+fileSuffix;
	}

    6. 在上面的上传图片实现中,图片上传后会保存在运行应用系统的服务器中,但若是是应用系统分布式部署环境下图片引用会出现问题,并且图片的上传下载会给服务器增长额外的压力跨域

因此在实际开发中会采起分布式部署,保存用户上传下载文件的服务器不会和系统应用部署的服务器处于同一台服务器,而是分开部署的,这时就须要一些网络传输代码来进行两个服务器之间的数据上传与下载,通常采用FTP协议。这里不作具体实现

2. SpringMVC中实现json格式数据交互

    1. 客户端与服务端的json数据交互过程:

  • 客户端向服务端发送请求,请求中带有一些数据:通常来讲,若是不进行特殊设置,提交的数据格式都为key/value格式的,好比form表单提交的字符串数据,请求中表示数据内容类型的请求头值为 contentType=application/x-www-form-urlen;但若是发送的数据为json格式,那么就须要设置请求头contentType=application/json
  • 服务端接受数据:若是请求中的数据格式类型是key/value,那么只须要按照一些参数绑定规则便可经过Controller中的方法参数进行接收处理;但若是是接收一个json格式的字符串数据,若是想要将该字符串转为Java中的对象(简单类型、pojo类型),那么就须要@RequestBody注解
  • 服务端向客户端页面响应json格式数据:必须经过@ResponseBody将java对象转成json串输出,在客户端页面中对该json串进行解析

    2. 若是是请求的数据格式为json、响应的数据格式也为json,在前端页面中须要将请求的内容转成json,不太方便,因此经常使用请求的数据格式为key/value、响应的数据格式为json。

    3. 使用json的缘由就在于:json数据格式在接口调用中、html页面中较经常使用,json格式简单,解析方便。好比Ajax异步数据交互时经常使用json、还有最重要JSONP跨域请求数据资源

    4. 简单使用示例:

  • 首先必须导入json与java对象转换的相关jar包
  • 定义一个html页面,经过jQuery框架中的ajax方法提交json数据,并接受返回响应的json数据
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
        <title>test page</title>
    	<meta http-equiv="pragma" content="no-cache">
    	<meta http-equiv="cache-control" content="no-cache">
    	<meta http-equiv="expires" content="0">    
    	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    	<meta http-equiv="description" content="This is my page">
    	<script type="text/javascript" src="../js/jquery-1.4.4.min.js"></script>
    	<script type="text/javascript">
    	//设置提交的数据格式为json,即请求头contentType=application/json,接收json数据
    		function requestJson(){
    			$.ajax({
    				type:'post',
    				url:'http://localhost:8081/user_manage/requestJson',
    				contentType:'application/json;charset=utf-8',
    				data:'{userAccount:"123456",userName="123",userPassword="123456"}',
    				success:function(data){
    					alert(data);
    				},
    				error:function(){
    					alert("请求失败");
    				}
    			});
    		}
    		//默认提交的数据格式为key/value,即请求头contentType=application/x-www-form-urlen,接收json数据
    		function requestKeyValue(){
    			$.ajax({
    				type:'post',
    				url:'http://localhost:8081/user_manage/requestKeyValue',
    				data:"userAccount=123456&userName=123&userPassword=123456",
    				success:function(data){
    					alert(data);
    				},
    				error:function(){
    					alert("请求失败");
    				}
    			});		
    		}
    	</script>
      </head>
      <body>
        <button onclick="requestJson()">提交json数据</button>
        <button onclick="requestKeyValue()">提交key/value数据</button>
      </body>
    </html>

     

  • Controller方法中接受json数据并绑定到一个pojo对象中,响应json数据
    @RequestMapping("/requestJson")
    	@ResponseBody//该注解就会把返回的对象user转为json字符串,而@RequestBody会把json字符串绑定到user形参中
    	public User JsonTest1(@RequestBody User user){
    		user.setUserPassword("12345678");
    		return user;
    	}
    
    	@RequestMapping("/requestKeyValue")
    	@ResponseBody
    	public User JsonTest2(User user){
    		user.setUserPassword("12345678");
    		return user;
    	}

     

3. SpringMVC对RESTful格式请求路径的支持

    1. 何为RESTful格式路径:

  • 不使用RESTful格式的请求路径:好比https://my.oschina.net/ProgramerLife/blog/write/draft?id=2275002
  • 使用RESTful格式的请求路径:则为https://my.oschina.net/ProgramerLife/blog/write/draft/2275002
  • 也就是将参数写在请求路径中,在服务端经过请求路径提取参数,而不是把参数放在请求体中,做为请求参数发送给服务端

    2. 实现一个需求做为示例:在用户管理界面,实现删除用户

    2. 首先要在web.xml中配置好用于接收RESTful格式请求路径的前端控制器:

<!-- RESTful风格的路径配置 -->
	<servlet>
		<servlet-name>RESTful</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!--SpringMVC加载的配置文件,这里是springweb应用上下文 -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:conf/springMVC.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>RESTful</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

    2. 请求路径为:http://localhost:8081/user_manage/delete/1/20/1000000001

    3. 服务端Controller方法:page表示该用户信息所在的页数,rows表示每页最大显示的用户信息数量,userAccount表示用户帐号

@RequestMapping("/delete/{page}/{rows}/{userAccount}")
    @ResponseBody
	public String deleteUser(@PathVariable(value="page") int page,
			@PathVariable(value="userAccount") int userAccount,
			@PathVariable(value="rows") int rows){
		userService.deleteUser(userAccount);
		DataGridResult users=userService.findUserList(page, rows);
		return users;
	}
  • @RequestMapping("/delete/{page}/{rows}/{userAccount}"):在@RequestMapping后所写的路径中,用{xxx}表示请求路径中哪几部分表明请求参数,也就是{xxx}至关于占位符
  • @PathVariable(value="page") int page:@PathVariable该注解用于Controller方法中用于接收请求参数的形参以前,表示将请求URL中的模板变量映射到功能处理方法的参数上。若是形参名和{xxx}中的值相同,@PathVariable则不用指定(value="page")来映射在路径中的数据

    4. RESTful格式请求路径所带来的问题:配置前端控制器的url-parttern中指定/,对静态资源的解析出现问题,由于此时前端控制器对于静态资源的请求路径也会拦截并在控制器映射器中寻找对应的Controller,而此时是没法寻找到对应的Controller的,因此会报404

    5. 解决RESTful格式请求路径对静态资源的解析出现的问题:

  • 在Spring3.*版本中,能够在SpringMVC的配置文件添加对静态资源的映射,好比
    <mvc:resources location="/WEB-INF/js/" mapping="/js/**"/>
  • 而在Spring4.*版本以上,不能使用上面的resources,能够在SpringMVC的配置文件添加另外一行配置:
    <mvc:default-servlet-handler/>

    可是该配置必须保证静态资源文件在webapp目录下,而经过resources配置的能够将静态资源放在WEB-INF目录下

4. SpringMVC 拦截器

    1. 自定义拦截器:实现org.springframework.web.servlet.HandlerInterceptor接口便可

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
//定义拦截器,实现HandlerInterceptor接口。接口中提供三个方法。
public class HandlerInterceptor1 implements HandlerInterceptor {
	//进入 Handler(即Controller)方法以前执行
	//用于身份认证、身份受权
	//好比身份认证,若是认证经过表示当前用户没有登录,须要此方法拦截再也不向下执行
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		//return false表示拦截,不向下执行
		//return true表示放行
		return false;
	}
	//进入Handler方法以后,返回modelAndView以前执行
	//应用场景从modelAndView出发:将公用的模型数据(好比菜单导航)在这里传到视图,也能够在这里统一指定视图
	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
	}
	//完成执行Handler后执行此方法
	//应用场景:统一异常处理,统一日志处理
	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
	}
}

    2. 配置全局拦截器:springmvc配置全局的拦截器,springmvc框架将配置的拦截器注入到每一个处理器映射器中,springmvc拦截器针对HandlerMapping(处理器映射器)进行拦截设置,或者说会将拦截器配置处处理器映射器中,若是某个请求路径该HandlerMapping映射成功(找到对应的Handler),那么会先被拦截器进行拦截处理,在处理器返回响应以前在进行一次处理。在SpringMVC的配置文件(springmvc.xml)中添加以下配置

<!-- 若是配置多个拦截器,在mvc:interceptors中添加多个mvc:interceptor便可 -->
	<mvc:interceptors>
		<mvc:interceptor>
			<!-- 拦截器类 -->
			<bean class="user_manage.interceptor.HandlerInterceptor1"></bean>
			<!-- 拦截器拦截的请求路径,/**表示拦截全部的url -->
			<mvc:mapping path="/**"/>
		</mvc:interceptor>
		<mvc:interceptor>
			<!-- 拦截器类 -->
			<bean class="user_manage.interceptor.HandlerInterceptor2"></bean>
			<!-- 拦截器拦截的请求路径,/**表示拦截全部的url -->
			<mvc:mapping path="/**"/>
		</mvc:interceptor>
	</mvc:interceptors>

    3. 拦截器执行顺序:若是配置了多个拦截器,那么就按照配置中所写拦截器的顺序来依次进行拦截,只有前一个拦截器对请求放行以后,后一个拦截器才能继续拦截。

    4. 拦截器使用实例:登陆认证拦截,除了登陆请求和注册请求以外,其余请求必须经过登陆认证拦截器拦截

public class LoginInterceptor implements HandlerInterceptor {

	
	//进入 Handler方法以前执行
	//用于身份认证、身份受权
	//好比身份认证,若是认证经过表示当前用户没有登录,须要此方法拦截再也不向下执行
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		
		//获取请求的url
		String url = request.getRequestURI();
		//判断url是不是公开 地址(实际使用时将公开 地址配置配置文件中)
		//这里公开地址是登录提交的地址
		if(url.indexOf("login")>=0){
			//若是是登录请求的路径地址,放行
			return true;
		}
		
		//判断session
		HttpSession session  = request.getSession();
		//从session中取出用户身份信息
		String username = (String) session.getAttribute("username");
		
		if(username != null){
			//身份存在,放行
			return true;
		}
		
		//执行这里表示用户身份须要认证,跳转登录页面
		request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
		
		//return false表示拦截,不向下执行
		//return true表示放行
		return false;
	}
相关文章
相关标签/搜索