- SpringMVC 基礎(chǔ) 查看鏈接
一、AJAX
1.1 簡介
AJAX = Asynchronous JavaScript and XML(異步的 JavaScript 和 XML);
AJAX 是一種在無需重新加載整個網(wǎng)頁的情況下,能夠更新部分網(wǎng)頁的技術(shù);
AJAX 不是一種新的編程語言,而是一種用于創(chuàng)建更好更快,以及交互性更強的 Web 應(yīng)用程序的技術(shù);
在 2005 年,Google 通過其 Google Suggest 使 AJAX 變得流行起來,Google Suggest 能夠自動完成搜索單詞;
-
Google Suggest 使用 AJAX 創(chuàng)造出動態(tài)性極強的 web 界面:當我們在谷歌的搜索框輸入關(guān)鍵字時,JavaScript 會把這些字符發(fā)送到服務(wù)器,然后服務(wù)器會返回一個搜索建議的列表,如百度的搜索框:
傳統(tǒng)的網(wǎng)頁(即不用 AJAX 技術(shù)的網(wǎng)頁)想要更新內(nèi)容或者提交一個表單,都需要重新加載整個網(wǎng)頁;
使用 AJAX 技術(shù)的網(wǎng)頁,通過在后臺服務(wù)器,進行少量的數(shù)據(jù)交換,就可以實現(xiàn)異步、局部更新;
使用 AJAX,用戶可以創(chuàng)建接近本地桌面應(yīng)用的直接、高可用、更豐富、更動態(tài)的 Web 用戶界面;
1.2 偽造 AJAX
-
可以使用前端的一個標簽,來偽造一個 ajax 的樣子:
iframe 標簽- 新建模塊,并導(dǎo)入 web 支持;
- 配置
web.xml及 springMVC 配置文件,運行 Tomcat 測試環(huán)境; - 編寫 html 頁面,使用 iframe 測試,感受下效果:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>iframe偽造ajax</title> <script> window.onload = function () { let myDate = new Date(); document.getElementById("currentTime").innerText = myDate.getTime(); }; function LoadPage() { let url = document.getElementById("url").value; document.getElementById("iframe1").src = url; }; </script> </head> <body> <div> <p>請輸入要加載的地址:<span id="currentTime"></span></p> <p> <input id="url" type="text" value="https://juejin.cn/"/> <input type="submit" value="提交" onclick="LoadPage()"/> </p> </div> <div> <h3>加載頁面位置:</h3> <iframe id="iframe1" style="width: 100%;height: 500px;"></iframe> </div> </body> </html>- 使用 IDEA 打開瀏覽器進行測試;
小結(jié):
- 利用 AJAX 可以實現(xiàn):
- 注冊時,輸入用戶名,自動檢測用戶是否已經(jīng)存在;
- 登陸時,提示用戶名,密碼錯誤;
- 刪除數(shù)據(jù)行時,將行 ID 發(fā)送到后臺,后臺在數(shù)據(jù)庫中刪除,數(shù)據(jù)庫刪除成功后,在頁面 DOM 中,將數(shù)據(jù)行也刪除;
- ...等等;
1.3 jQuery.ajax
- jQuery 3.6.0 開發(fā)版 下載鏈接
- Ajax 的核心是 XMLHttpRequest 對象(XHR):
- XHR 為向服務(wù)器發(fā)送請求和解析服務(wù)器響應(yīng),提供了接口,能夠以異步方式,從服務(wù)器獲取新數(shù)據(jù);
- jQuery 提供多個與 AJAX 有關(guān)的方法;
- 通過 jQuery AJAX 方法,能夠使用 HTTP Get 和 HTTP Post,從遠程服務(wù)器上請求文本、HTML、XML 或 JSON,同時能夠把這些外部數(shù)據(jù),直接載入網(wǎng)頁的被選元素中;
- jQuery(是一個庫) 不是生產(chǎn)者,而是大自然搬運工;
- jQuery Ajax 本質(zhì)就是 XMLHttpRequest,對它進行了封裝,方便調(diào)用:
jQuery.ajax(...)
/*
部分參數(shù):
url:請求地址
type:請求方式,GET、POST(1.9.0之后用method)
headers:請求頭
data:要發(fā)送的數(shù)據(jù)
contentType:即將發(fā)送信息至服務(wù)器的內(nèi)容編碼類型(默認: "application/x-www-form-urlencoded; charset=UTF-8")
async:是否異步
timeout:設(shè)置請求超時時間(毫秒)
beforeSend:發(fā)送請求前執(zhí)行的函數(shù)(全局)
complete:完成之后執(zhí)行的回調(diào)函數(shù)(全局)
success:成功之后執(zhí)行的回調(diào)函數(shù)(全局)
error:失敗之后執(zhí)行的回調(diào)函數(shù)(全局)
accepts:通過請求頭發(fā)送給服務(wù)器,告訴服務(wù)器當前客戶端可接受的數(shù)據(jù)類型
dataType:將服務(wù)器端返回的數(shù)據(jù)轉(zhuǎn)換成指定類型
"xml": 將服務(wù)器端返回的內(nèi)容轉(zhuǎn)換成xml格式
"text": 將服務(wù)器端返回的內(nèi)容轉(zhuǎn)換成普通文本格式
"html": 將服務(wù)器端返回的內(nèi)容轉(zhuǎn)換成普通文本格式,在插入DOM中時,如果包含JavaScript標簽,則會嘗試去執(zhí)行。
"script": 嘗試將返回值當作JavaScript去執(zhí)行,然后再將服務(wù)器端返回的內(nèi)容轉(zhuǎn)換成普通文本格式
"json": 將服務(wù)器端返回的內(nèi)容轉(zhuǎn)換成相應(yīng)的JavaScript對象
"jsonp": JSONP 格式使用 JSONP 形式調(diào)用函數(shù)時,如 "myurl?callback=?" jQuery 將自動替換 ? 為正確的函數(shù)名,以執(zhí)行回調(diào)函數(shù)
*/
測試一:HttpServletResponse 實現(xiàn)
- 使用最原始的 HttpServletResponse 處理,最簡單,最通用:
- 配置
web.xml和 springmvc 的配置文件:- 注意:靜態(tài)資源過濾和注解驅(qū)動配置;
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--配置SpringMVC-->
<!-- 1. 開啟SpringMVC注解驅(qū)動,注意導(dǎo)入mvc的頭文件-->
<mvc:annotation-driven/>
<!--2. 靜態(tài)資源過濾-->
<mvc:default-servlet-handler/>
<!--3. 掃描包:Controller-->
<context:component-scan base-package="com.study.controller"/>
<!--4. 視圖解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
<!--前綴-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后綴-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 創(chuàng)建 Controll:AjaxController
// 不走視圖頁面,直接返回字符串
@RestController
public class AjaxController {
@RequestMapping("/a1")
public void ajax1(String name, HttpServletResponse response) throws IOException {
System.out.println("前端傳遞參數(shù):" + name);
if ("admin".equals(name)) {
response.getWriter().println("true");
} else {
response.getWriter().println("false");
}
}
}
- 導(dǎo)入 jQuery,可以使用在線的 CDN,也可以下載導(dǎo)入:
<%--<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>--%>
<script src="${pageContext.request.contextPath}/static/js/jquery-3.6.0.js"></script>
- 編寫 index.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
<%--<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>--%>
<script src="${pageContext.request.contextPath}/static/js/jquery-3.6.0.js"></script>
<script>
function a1() {
// post、get都是調(diào)用的ajax方法,可以直接用 $.ajax
$.post({
url: "${pageContext.request.contextPath}/a1",
// data:傳遞給后端的數(shù)據(jù),鍵值對的形式 name對應(yīng)后端的name
data: {"name": $("#userName").val()},
// data:后端返回的數(shù)據(jù) status:狀態(tài)
success: function (data, status) {
console.log(data);
console.log(status);
}
});
}
</script>
</head>
<body>
<%--onblur:失去焦點觸發(fā)事件--%>
用戶名:<input type="text" id="userName" onblur="a1()"/>
</body>
</html>
-
啟動 Tomcat 測試:
- 打開瀏覽器的控制臺,當鼠標離開輸入框時,可以看到發(fā)出了一個 ajax 的請求;
-
流程分析:
測試二:SpringMVC、JSON 方式實現(xiàn)
- 需要導(dǎo)入 Jackson 依賴:否則請求路徑時,報錯 406
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2.2</version>
</dependency>
- 創(chuàng)建實體類:user
- 使用 lombok,需要事先導(dǎo)入依賴;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
private String sex;
}
- 在 Controller 類中添加方法:
- 獲取一個集合對象,展示到前端頁面;
@RequestMapping("/a2")
public List<User> ajax2() {
List<User> list = new ArrayList<>();
list.add(new User("test01", 20, "男"));
list.add(new User("test02", 21, "女"));
list.add(new User("test03", 20, "男"));
// 類中使用@RestController注解,將list轉(zhuǎn)成json格式返回
return list;
}
- 前端頁面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%--jQuery--%>
<script src="${pageContext.request.contextPath}/static/js/jquery-3.6.0.js"></script>
<script>
$(function () {
$("#btn").click(function () {
/*
簡寫方式:
$.post(url,param[可以省略(傳遞給后端的參數(shù))],success[回調(diào)函數(shù)])
*/
$.post("${pageContext.request.contextPath}/a2", function (data) {
console.log(data);
let html = "";
for (let i = 0; i < data.length; i++) {
<%-- jsp中需要轉(zhuǎn)義es6模板字符串${}:\${} --%>
html += `<tr>
<td>\${data[i].name}</td>
<td>\${data[i].age}</td>
<td>\${data[i].sex}</td>
</tr>`
}
$("#content").html(html);
});
/*
$.post({
url: "${pageContext.request.contextPath}/a2",
success:function (data){
let html = "";
for (let i = 0; i < data.length; i++) {
html += `<tr>
<td>\${data[i].name}</td>
<td>\${data[i].age}</td>
<td>\${data[i].sex}</td>
</tr>`
}
$("#content").html(html);
}
});
*/
})
})
</script>
</head>
<body>
<input type="button" id="btn" value="獲取數(shù)據(jù)"/>
<table>
<tr>
<td>姓名</td>
<td>年齡</td>
<td>性別</td>
</tr>
<tbody id="content"></tbody>
</table>
</body>
</html>
-
運行測試:
1.4 AJAX 注冊提示
- 在 Controller 類中,添加方法:
@RequestMapping("/a3")
public String ajax3(String name, String pwd) {
String msg = "";
if (name != null) {
// admin:模擬數(shù)據(jù)庫查詢的數(shù)據(jù)
if ("admin".equals(name)) {
msg = "OK";
} else {
msg = "用戶名有誤";
}
}
if (pwd != null) {
// 123456:模擬數(shù)據(jù)庫查詢的數(shù)據(jù)
if ("123456".equals(pwd)) {
msg = "OK";
} else {
msg = "密碼有誤";
}
}
// 類中使用@RestController注解,將msg轉(zhuǎn)成json格式返回
return msg;
}
- 創(chuàng)建前端頁面:
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>login</title>
<script src="${pageContext.request.contextPath}/static/js/jquery-3.6.0.js"></script>
<script>
function a1() {
$.post({
url: "${pageContext.request.contextPath}/a3",
data: {"name": $("#name").val()},
success: function (data) {
if (data.toString() === "OK") {
$("#userInfo").css("color", "green");
}else {
$("#userInfo").css("color", "red");
}
$("#userInfo").html(data);
}
});
}
function a2() {
$.post({
url: "${pageContext.request.contextPath}/a3",
data: {"pwd": $("#pwd").val()},
success: function (data) {
if (data.toString() === "OK") {
$("#pwdInfo").css("color", "green");
}else {
$("#pwdInfo").css("color", "red");
}
$("#pwdInfo").text(data);
}
});
}
</script>
</head>
<body>
<p>
<%--onblur:失去焦點觸發(fā)事件--%>
用戶名:<input type="text" id="name" onblur="a1()"/>
<span id="userInfo"></span>
</p>
<p>
密碼:<input type="text" id="pwd" onblur="a2()"/>
<span id="pwdInfo"></span>
</p>
</body>
</html>
- 處理 JSON 亂碼:SpringMVC 配置文件中進行配置
<!--JSON亂碼統(tǒng)一解決(固定代碼)-->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
-
運行測試:
1.5 獲取 baidu 接口 Demo
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>JSONP百度搜索</title>
<style>
#q {
width: 500px;
height: 30px;
border: 1px solid #ddd;
line-height: 30px;
display: block;
margin: 0 auto;
padding: 0 10px;
font-size: 14px;
}
#ul {
width: 520px;
list-style: none;
padding: 0;
border: 1px solid #ddd;
margin: -1px auto 0;
display: none;
}
#ul li {
line-height: 30px;
padding: 0 10px;
}
#ul li:hover {
background-color: #f60;
color: #fff;
}
</style>
<script>
// 2.步驟二
// 定義demo函數(shù) (分析接口、數(shù)據(jù))
function demo(data) {
let Ul = document.getElementById('ul');
let html = '';
// 如果搜索數(shù)據(jù)存在 把內(nèi)容添加進去
if (data.s.length) {
// 隱藏掉的ul顯示出來
Ul.style.display = 'block';
// 搜索到的數(shù)據(jù)循環(huán)追加到li里
for (let i = 0; i < data.s.length; i++) {
// es6 模板字符串
html += `<li>${data.s[i]}</li>`
// html += '<li>' + data.s[i] + '</li>';
}
// 循環(huán)的li寫入ul
Ul.innerHTML = html;
}
}
// 1.步驟一
window.onload = function () {
// 獲取輸入框和ul
let Q = document.getElementById('q');
let Ul = document.getElementById('ul');
// 事件鼠標抬起時候
Q.onkeyup = function () {
// 如果輸入框不等于空
if (this.value != '') {
// ☆☆☆☆☆☆JSONPz重點☆☆☆☆☆☆
// 創(chuàng)建標簽
let script = document.createElement('script');
// 給定要跨域的地址 賦值給src
// 這里是要請求的跨域的地址 百度搜索的跨域地址
script.src = 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=' + this.value + '&cb=demo';
// 將組合好的帶src的script標簽追加到body里
document.body.appendChild(script);
}
}
}
</script>
</head>
<body>
<input type="text" id="q"/>
<ul id="ul">
</ul>
</body>
</html>
二、攔截器
2.1 概述
- SpringMVC 的處理器,攔截器,類似于 Servlet 開發(fā)中的過濾器 Filter,用于對處理器進行預(yù)處理和后處理,可以自定義攔截器,實現(xiàn)特定的功能;
- 過濾器與攔截器的區(qū)別:攔截器是 AOP 思想的具體應(yīng)用;
-
過濾器:
- servlet 規(guī)范中的一部分,任何 Java web 工程都可以使用;
- 在
url-pattern中配置了/*之后,可以對所有要訪問的資源,進行攔截;
-
攔截器:
- 攔截器是 SpringMVC 框架自有的,只有使用了 SpringMVC 框架的工程才能使用;
- 攔截器只會攔截訪問控制器方法,如果訪問的是 jsp、html、css、image、js,是不會進行攔截的;
2.2 自定義攔截器
- 要自定義攔截器,必須實現(xiàn) HandlerInterceptor 接口;
- 創(chuàng)建新模塊,并添加 web 支持;
- 配置
web.xml和 springmvc 配置文件; - 創(chuàng)建攔截器:config 目錄下,創(chuàng)建 MyInterceptor
public class MyInterceptor implements HandlerInterceptor {
/*
如果返回true執(zhí)行下一個攔截器
如果返回false就不執(zhí)行下一個攔截器
*/
// 在請求處理的方法之前執(zhí)行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("------------處理前------------");
return HandlerInterceptor.super.preHandle(request, response, handler);
}
// 在請求處理方法執(zhí)行之后執(zhí)行(一般用于處理日志)
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("------------處理后------------");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
// 在dispatcherServlet處理后執(zhí)行,做清理工作
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("------------清理------------");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
- 在 SpringMVC 的配置文件中,配置攔截器:
<!--攔截器配置-->
<mvc:interceptors>
<mvc:interceptor>
<!--
/**:包括路徑及其子路徑
/admin/*:攔截的是/admin/add等等這種,/admin/add/user不會被攔截
/admin/**:攔截的是/admin/下的所有
-->
<mvc:mapping path="/**"/>
<!--bean配置的就是攔截器-->
<bean class="com.study.config.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
- 創(chuàng)建 Controller,接收請求:
@RestController
public class TestController {
@RequestMapping("/interceptor")
public String test2() {
System.out.println("控制器中的方法執(zhí)行了");
return "hello";
}
- 編寫前端:index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/interceptor">攔截器測試</a>
</body>
</html>
-
配置 Tomcat,啟動測試:
2.3 驗證用戶是否登錄 (認證用戶)
實現(xiàn)思路:
- 有一個登陸頁面,需要寫一個 controller 訪問頁面;
- 登陸頁面有一個提交表單的動作,需要在 controller 中處理,判斷用戶名、密碼是否正確,如果正確,向 session 中寫入用戶信息,返回登陸成功信息;
- 攔截用戶請求,判斷用戶是否登陸,如果用戶已經(jīng)登陸,放行,如果用戶未登陸,跳轉(zhuǎn)到登陸頁面;
測試:
- 創(chuàng)建登陸頁面:
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首頁</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/user/toLogin">登錄</a>
<a href="${pageContext.request.contextPath}/user/toSuccess">成功頁面</a>
</body>
</html>
- 創(chuàng)建 Controller 類,處理請求:
@Controller
@RequestMapping("/user")
public class LoginController {
// 跳轉(zhuǎn)到登錄頁面
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
// 跳轉(zhuǎn)到成功頁面
@RequestMapping("/toSuccess")
public String toSuccess() {
return "success";
}
// 登陸提交
@RequestMapping("/login")
public String login(HttpSession session, String username, String pwd) {
// 把用戶的信息存在Session中
session.setAttribute("user", username);
return "success";
}
// 退出登陸
@RequestMapping("/logout")
public String logout(HttpSession session) {
// 移除Session
session.removeAttribute("user");
// 手動注銷Session
// session.invalidate();
return "login";
}
}
- 創(chuàng)建登陸成功的頁面:
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登錄成功</title>
</head>
<body>
<h3>登錄成功頁面</h3>
<hr>
${user}
<a href="${pageContext.request.contextPath}/user/logout">注銷</a>
</body>
</html>
- 在 index 頁面上測試跳轉(zhuǎn),啟動 Tomcat 測試,未登錄也可以進入主頁:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首頁</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/user/toLogin">登錄</a>
<a href="${pageContext.request.contextPath}/user/toSuccess">成功頁面</a>
</body>
</html>
- 創(chuàng)建登錄攔截器:
public class LoginInterceptor implements HandlerInterceptor {
// 在請求處理的方法之前執(zhí)行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 如果是登陸頁面則放行
if (request.getRequestURI().contains("toLogin")) {
return true;
}
if (request.getRequestURI().contains("login")) {
return true;
}
HttpSession session = request.getSession();
// 如果用戶已登陸也放行
if (session.getAttribute("user") != null) {
return true;
}
// 用戶沒有登陸跳轉(zhuǎn)到登陸頁面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
}
- 在 SpringMVC 的配置文件中,注冊攔截器:
<!--攔截器配置-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<!--bean配置的就是攔截器-->
<bean class="com.study.config.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
- 再次重啟 Tomcat 測試,可以實現(xiàn)登錄攔截功能;
三、文件上傳和下載
3.1 概述
- 文件上傳是項目開發(fā)中最常見的功能之一,springMVC 可以很好的支持文件上傳,但是 SpringMVC 上下文中,默認沒有裝配 MultipartResolver,因此,默認情況下,不能處理文件上傳工作;
- 如果想使用 Spring 的文件上傳功能,則需要在上下文中配置MultipartResolver;
- 前端表單要求:為了能上傳文件,必須將表單的 method 設(shè)置為 POST,并將 enctype 設(shè)置為 multipart/form-data,只有在這樣的情況下,瀏覽器才會把用戶選擇的文件,以二進制數(shù)據(jù)發(fā)送給服務(wù)器;
- 表單中的 enctype 屬性說明:
- application/x-www=form-urlencoded:默認方式,只處理表單域中的 value 屬性值,采用這種編碼方式的表單,會將表單域中的值處理成 URL 編碼方式;
- multipart/form-data:以二進制流的方式,來處理表單數(shù)據(jù),這種編碼方式,會把文件域指定文件的內(nèi)容,也封裝到請求參數(shù)中,不會對字符編碼;
- text/plain:除了把空格轉(zhuǎn)換為
+號外,其他字符都不做編碼處理,這種方式適用 直接通過表單發(fā)送郵件;
<!--編碼方式:文件上傳-->
<form action="" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit">
</form>
- 一旦設(shè)置了enctype 為 multipart/form-data,瀏覽器即會采用二進制流的方式,來處理表單數(shù)據(jù),而對于文件上傳的處理,則涉及在服務(wù)器端解析原始的 HTTP 響應(yīng);
- 在 2003 年,Apache Software Foundation 發(fā)布了開源的 Commons FileUpload 組件,很快成為 Servlet/JSP 程序員上傳文件的最佳選擇;
- Servlet3.0 規(guī)范已經(jīng)提供方法來處理文件上傳,但這種上傳需要在 Servlet 中完成;
- Spring MVC 提供了更簡單的封裝;
- Spring MVC 為文件上傳,提供了直接的支持,這種支持是用 即插即用 的 MultipartResolver 實現(xiàn)的;
- Spring MVC 使用 Apache Commons FileUpload 技術(shù)實現(xiàn)了一個 MultipartResolver 實現(xiàn)類
CommonsMultipartResolver因此,SpringMVC 的文件上傳,還需要依賴 Apache Commons FileUpload 的組件;
3.2 文件上傳
- 創(chuàng)建新模塊,并添加 web 框架支持;
- 配置
web.xml及 SpringMVC 配置文件; - 配置 Tomcat,啟動測試環(huán)境;
- 導(dǎo)入文件上傳的 jar 包,commons-fileupload 或 Maven 依賴:
<!--文件上傳:自動導(dǎo)入commons-io-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!--servlet-api導(dǎo)入高版本的-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
- 在 SpringMVC 配置文件中配置 bean:
multipartResolver- id 必須為:multipartResolver,否則報400錯誤;
<!--文件上傳配置:id必須為:multipartResolver,否則報400錯誤-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 請求的編碼格式,必須和jsp的pageEncoding屬性一致,以便正確讀取表單的內(nèi)容,默認為ISO-8859-1 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上傳文件大小上限,單位為字節(jié)(10485760=10M) -->
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
- CommonsMultipartFile 的常用方法:
- String getOriginalFilename():獲取上傳文件的原名;
- InputStream getInputStream():獲取文件流;
- void transferTo(File dest):將上傳文件,保存到一個目錄文件中;
- 編寫前端頁面:
index.jsp
<form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit" value="upload">
</form>
- 創(chuàng)建 Controller 類:FileController
上傳方式一:
@Controller
public class FileController {
// 上傳方式 1:
// @RequestParam("file"):將name=file控件得到的文件封裝成CommonsMultipartFile對象
// 批量上傳,CommonsMultipartFile則為數(shù)組即可
@RequestMapping("/upload")
public String fileUpload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
// 獲取文件名:file.getOriginalFilename();
String uploadFileName = file.getOriginalFilename();
// 如果文件名為空,直接回到首頁!
if ("".equals(uploadFileName)) {
return "redirect:/index.jsp";
}
System.out.println("上傳文件名 : " + uploadFileName);
// 上傳路徑保存設(shè)置
String path = request.getServletContext().getRealPath("/upload");
// 如果路徑不存在,創(chuàng)建一個
File realPath = new File(path);
if (!realPath.exists()) {
realPath.mkdir();
}
System.out.println("上傳文件保存地址:" + realPath);
// 文件輸入流
InputStream is = file.getInputStream();
// 文件輸出流
OutputStream os = new FileOutputStream(new File(realPath, uploadFileName));
// 讀取寫出
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
os.flush();
}
os.close();
is.close();
return "redirect:/index.jsp";
}
}
- 測試上傳文件;
上傳方式二:
- 采用
file.transferTo來保存上傳的文件; - 在 FileController 類中添加方法:
/*
上傳方式 2:
采用file.transferTo來保存上傳的文件
*/
@RequestMapping("/upload2")
public String fileUpload2(@RequestParam("file") CommonsMultipartFile file,
HttpServletRequest request) throws IOException {
// 上傳路徑保存設(shè)置
String path = request.getServletContext().getRealPath("/upload");
File realPath = new File(path);
if (!realPath.exists()) {
realPath.mkdir();
}
// 上傳文件地址
System.out.println("上傳文件保存地址:" + realPath);
// 通過CommonsMultipartFile的方法直接寫文件(注意這個時候)
file.transferTo(new File(realPath + "/" +
file.getOriginalFilename()));
return "redirect:/index.jsp";
}
- 修改前端表單提交地址;
- 運行測試;
3.3 文件下載
-
文件下載步驟:
- 設(shè)置 response 響應(yīng)頭;
- 讀取文件:InputStream;
- 寫出文件:OutputStream;
- 執(zhí)行操作;
- 關(guān)閉流(先開后關(guān));
代碼實現(xiàn):
// 文件下載
@RequestMapping("/download")
public String downloads(HttpServletResponse response, HttpServletRequest request) throws Exception {
// 要下載的圖片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "1.png";
// 1. 設(shè)置response 響應(yīng)頭
// 設(shè)置頁面不緩存,清空buffer
response.reset();
// 字符編碼
response.setCharacterEncoding("UTF-8");
// 二進制傳輸數(shù)據(jù)
response.setContentType("multipart/form-data");
// 設(shè)置響應(yīng)頭
response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));
File file = new File(path, fileName);
// 2. 讀取文件:輸入流
InputStream input = new FileInputStream(file);
// 3. 寫出文件:輸出流
OutputStream out = response.getOutputStream();
byte[] buff = new byte[1024];
int index = 0;
// 4. 執(zhí)行寫出操作
while ((index = input.read(buff)) != -1) {
out.write(buff, 0, index);
out.flush();
}
out.close();
input.close();
return null;
}
-
創(chuàng)建對應(yīng)目錄及需要下載的文件:
對應(yīng)前端頁面:
<a href="${pageContext.request.contextPath}/download">點擊下載</a>
- 運行測試;
四、SSM 回顧







