1. 實現圖片上傳
需求:在用戶註冊頁面實現上傳圖片作爲用戶頭像
1. springmvc中對多部件類型請求數據解析:在頁面form中提交enctype="multipart/form-data"的數據時,需要springmvc對multipart類型的數據進行解析。在springmvc.xml中配置multipart類型解析器。
<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服務器中創建圖片虛擬目錄用於存儲圖片:通過圖形化界面創建,
Document base就表示本地路徑,而path就表示瀏覽器訪問路徑;也可以直接修改tomcat的配置:在conf/server.xml文件,添加虛擬目錄 :
注意:在圖片虛擬目錄 中,一定將圖片目錄分級創建(提高i/o性能),一般我們採用按日期(年、月、日)進行分級創建。
3. jsp頁面中對上傳圖片代碼編寫:主要有三個要編寫的地方,form表單的enctype="multipart/form-data"和method="post"兩個屬性,以及<input type="file" name="">標籤上傳文件
<!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值相同
@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. 將接收的文件發送到圖片服務器的虛擬目錄中:
//保存圖片到服務器中,並生成該圖片的訪問路徑
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;
}