最近發現好久前一股腦地學習框架,發覺愈加迷糊.知道了框架只是暫時的,重點是基礎的技術.該文大篇幅回顧Servlet技術棧和簡要的MVC框架.html
至於爲何學J2EE,額,你們都用框架,可框架也是使用了標準的J2EE規範進行開發,好比SpringMVC的前端控制器是Servlet,Struts的Filter,Spring Boot項目內嵌了Tomcat 應用容器....前端
該文是自我學習總結,比較適合接觸Java Web編程不久的朋友閱讀,若是讀的沒意思就請直接棄之 :)java
我知道,我明白你知道MVC框架,但是我仍是叨嘮一下.mysql
Model,Java普通類對象,用來做爲信息存儲對象的模塊.web
View,服務器響應客戶端請求後生成頁面響應對象的模塊.算法
Controller,處理信息類型轉換以及執行業務的模塊.sql
簡單地說,就是將咱們上傳的信息與類型數據進行匹配轉換,以後都是瑣碎的加些什麼數據攔截器,過濾器之類的.數據庫
由於咱們大多數狀況下經過一張表單打到服務器,這時表單的數據都默認是String類型的數據,這時就不適應於類型數據工程語言(C++,PHP,Java,C#).express
因此,必須轉換類型.apache
那麼,有什麼技術可讓咱們獲取表單數據?以及獲取後咱們該處理?
HTTP協議就是一種讓咱們獲取和返回數據的技術
關於這方面的知識建議你去看看《圖解HTTP》,一本薄薄的書.這裏只是做爲引子作個簡單的說明.
HTTP創建於TCP協議之上,但其實能夠根據分層而選擇忽略底層原理.
HTTP規定了應用層的請求響應規則,客戶端<-->服務端的信息必須知足HTTP格式.
客戶端瀏覽器請求,
服務端響應請求,
把HTML,CSS,Javascript等信息存儲於HTTP對象載體,
響應返回至客戶端,
客戶端進行頁面渲染顯示.
Sun公司成爲制定Java語言的先行者,使Java語言適用於多種領域開發,動態Web開發領域一樣也給出了優秀的解決方案.
動態web技術--服務器根據客戶端不一樣的請求數據來生成不一樣的響應數據並做返回.
關於Servlet你須要弄清楚下面幾個概念,你將在閱讀完該文後掌握它們.
上面這張圖是Java Web體系的原型技術,也就是說其餘技術基本都構建在這些技術之上,它們是根基.
HTML,CSS,JavaScript三者做爲頁面渲染交互技術而存在;
JDBC做爲鏈接數據庫的鏈接技術,這樣能夠進行數據庫信息的獲取和存儲;
Tomcat做爲Servlet應用容器而存在,等待用戶請求;
Servlet做爲動態信息的Java處理類,可以將對應的Java數據結構轉化爲String拼接到HTML之中去;
JavaBean就是簡單的Java類,它有固定的格式,很容易就能寫出一個JavaBean;
Session存在是由於HTTP是無狀態協議,須要Session來做爲狀態標示;
Request/Response是Servlet容器抽象出來的請求/響應對象,以它來獲取數據和將數據寫入HTTP響應;
Java DataBase Connectivity是Java技術的核心之一,如今基本沒有不鏈接數據庫的web應用.
JDBC是一套Java定義的數據庫鏈接接口,實現部分由各大數據庫廠商進行開發.(你想要更大的市場,你就必須支持我)
軟件開發其中一項精髓是抽象,暫時擱置實現細節,拿來用就好了,除非你要去作該類產品的實現.
1.Java語言有接口,可是沒有提供實現,因此咱們必須先加載實現包.
2.經過鏈接管理器註冊驅動
3.獲取數據庫鏈接
4.獲得表明SQL語句的對象
5.執行語句和獲取結果
6.釋放佔用的資源
DriverManager
①註冊驅動
DriverManager.registerDriver(new com.mysql.jdbc.Driver());//依賴具體的驅動類,會致使驅動被註冊兩次
Class.forName("com.mysql.jdbc.Driver");//替代方案,在類被引入時自動註冊.
②獲取數據庫鏈接
DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
Connection
①獲取數據庫操做語句對象
conn.createStatement();
conn.prepareCall(sql);
conn.prepareStatement(sql,columnNames);
②事務處理
conn.commit();
conn.rollback();
Statement
①表明SQL語句
Statement s = conn.createStatement();
②執行語句,接收返回結果
ResultSet rs = s.executeQuery(sql);
s.executeUpdate(sql);
ResultSet
①查詢SQL所返回的結果集對象,用來遍歷操做
rs.next();
②遍歷後是一條記錄,能夠獲取記錄上的數據
PrepareStatement
①優於Statement,指示SQL語句的預編譯,提升數據庫執行效率
②防止SQL注入,直接對象對接語句
③語句參數使用佔位符?
1.配置文件
2.工具類
3.業務代碼
在web開發中,數據量分頁的狀況數不勝數,不一樣數據庫分頁語句不一樣,可是邏輯是同樣的.
分頁邏輯須要參數:數據總條數count(*),分頁大小size,當前頁面數current
Oracle
oracle中數據表中隱含了一個rownum字段,標識了每條記錄在表中的行號,利用它能獲取特定行數據.
select a1.* from (select student.*,rownum rn from student) a1 where rn between 3 and 5;
MySQL
MySQL中使用limit關鍵字來獲取數據行數
select * from customer limit 10,5;//第10行開始後的前5條數據
Page類
分頁邏輯實現
JDBC還有其餘一些重點知識,包括存儲過程調用,事務控制,數據庫鏈接池實現等.因爲篇幅問題不做詳述,用到的時候直接查一下資料就能找到.
前面理解了Servlet是一個特殊的Java類,經過應用服務器運行來處理請求信息,下面應該熟悉下面的兩張圖
Servlet核心類:
這個類圖在後面將反覆使用,請查看手冊,看一下方法名.
下面看一下流程圖:
看4,7,8每次你訪問服務器時,
Tomcat查詢web.xml,查找url-pattern.
服務器會生成一個Request對象和一個Response對象來承接請求信息和響應信息,
每次訪問會調用一次Servlet.service(),這個方法的邏輯就是整個響應請求的邏輯.
web app通常都會須要一些配置文件,在Java web應用中這個文件叫web.xml,它是用來配置該web app的.
Servlet聲明及映射就是配置URL映射的標籤,服務器查詢標籤知道我調的url是Servlet A仍是Servlet B處理.
Servlet能夠由應用服務器生成,默認生成一個DefaultServlet,或者是開發者指定一個繼承HttpServlet的類.
全部HTML,CSS,JavaScript等沒有指定Servlet的都將默認生成一個DefaultServlet來進行處理.
每一個訪問都會調用一次Servlet.service(),
/** * Receives standard HTTP requests from the public * <code>service</code> method and dispatches * them to the <code>do</code><i>Method</i> methods defined in * this class. This method is an HTTP-specific version of the * {@link javax.servlet.Servlet#service} method. There's no * need to override this method. * * @param req the {@link HttpServletRequest} object that * contains the request the client made of * the servlet * * @param resp the {@link HttpServletResponse} object that * contains the response the servlet returns * to the client * * @exception IOException if an input or output error occurs * while the servlet is handling the * HTTP request * * @exception ServletException if the HTTP request * cannot be handled * * @see javax.servlet.Servlet#service */ protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); } catch (IllegalArgumentException iae) { // Invalid date header - proceed as if none was set ifModifiedSince = -1; } if (ifModifiedSince < (lastModified / 1000 * 1000)) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }
根據HTTP請求的方法不一樣,調用相應的處理方法,如今通常只會使用到兩種請求方法,GET/POST
對應這個請求的Servlet,將調用doGet(req,resp),以此類推.
至此,咱們知道了關鍵的一點,關於Servlet編程,咱們只要繼承HttpServlet,重寫doGet和doPost方法等待調用就好了.
該怎麼重寫呢?思路是,表單的提交信息必定是封裝至HttpServletRequest對象中,咱們經過獲取信息後根據信息寫入至HttpServletResponse對象.
HttpServletRequest.getParameter(String name)能夠獲取表單信息,處理信息.
HttpServletResponse.getWriter()能夠獲取到一個PrintWriter對象,能夠將處理後相應的信息存儲在這個對象中,而後由服務器處理寫入HTTP報文主體中.
PrintWriter是能夠完成輸出操做,可是內容非常繁瑣,上圖簡單的頁面就必須須要打一大堆代碼.
public class MyServlet extends HttpServlet{ public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); out.println("<HTML>"); out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>"); out.println(" <BODY>"); out.println("</IMG src='hackr.gif' alt='hackr.jp" width='240' height='84'/> out.println(" </BODY>"); out.println("</HTML>"); out.flush(); out.close() } }
因此,Java官方推出了一個新的解決方案來替代前一方案,Servlet的應用服務器必須實現JSP編譯器,用來編譯jsp文件,將其轉換成Servlet文件.
一樣採用Servlet來進行響應處理,只不過將處理結果存於JSP文件中而後讓編譯器編譯成結果Servlet,結果Servlet來輸出信息至HTTP響應報文主體中.
這樣就將本來的Servlet職責(Controller,View)分給了Servlets(Controller)和JSPs(View)這兩個模塊.
JSP編譯後的文件存於tomcat/work目錄下.
新的解決方案就多出了許多新的問題,
①一個Servlet怎麼作到調用一個JSP文件(跳轉);
②JSP如何知曉本身須要顯示什麼數據;
③JSP文件規範;
Servlet跳轉就是以上的兩種方式進行,
方式一使用response.sendRedirect(url); //直接輸入跳轉的url
方式二使用request.getRequestDispatcher(url).forward(request,response);
//輸入跳轉url做爲定位,把request,response填入後進行跳轉
由於JSP文件本質上會被編譯成Servlet,因此可使用Servlet與jsp進行跳轉,只要把url填寫爲XXX.jsp便可.
jsp須要獲取信息來進行顯示,獲取的信息一定來自轉換交接的Servlet那.因此Jsp顯示什麼數據的問題在於它能獲取什麼數據,Servlet存儲了什麼數據.
Servlet域對象指的是Servlet用來存儲對象的區域,jsp能夠在這些區域中獲取數據,Servlet有三大域對象.
域對象存儲方法{域}.setAttribute("objectName",Object);
域對象獲取對象方法{域}.getAttribute("objectName");
ServletContext
這個域對象是全部Servlet的共用存儲域,你保存的對象全部的Servlet均可以獲取,jsp也是一種Servlet,因此它也能夠獲取.
Request
請求域對象,這個請求域在誰手裏,誰就能夠獲取.只要經過轉發這個Request,那麼Servlet就可拿到這個域裏面的對象.
Session
當客戶端進行第一次訪問時,應用服務器會爲你建立一個會話對象;
併爲你發送一個sessionID,這個SessionID會做爲Cookie保存於你的瀏覽器中,做爲訪問這個會話域的憑證;
你能夠在Session域中存取對象(用戶信息),直至Session被銷燬(通常是超時銷燬);
請注意,JSP文件規範很大成分參考引入博文,非本人原創.
下面咱們使用JSP2.3版本來看一下JSP的規範.
JSP頁面是動靜結合來展現HTML頁面內容的,靜就是靜態文件內容(HTML,CSS,JavaScript),動則是獲取操做數據的JSP頁面方法.
關於JSP你須要掌握如下內容以知足開發
腳本元素
JSP指令
JSP動做標籤
內置對象
表達式語言EL
JSTL標籤庫
由於JSP被編譯成Servlet,上述的動態語言元素都會被編譯後寫入靜態文件中,咱們經過編譯先後文件來學習這些內容.
<%! %> 聲明:定義翻譯後Servlet程序的 全局變量或全局方法.內部類
<%= %> 表達式 輸出內容到瀏覽器 效果等同於 out.print
<% %> 腳本代碼塊,嵌入java運行代碼 ---- 不翻譯
<%-- --%>JSP註釋,編譯成Servlet後消失
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <h1>JSP腳本元素</h1> <%! // JSP聲明 定義成員變量、成員方法 、內部類 public static void m(){} class A {} %> <!-- 表達式 等價於 會被翻譯爲 out.print --> <%="abcd" %> <% // JSP 腳本代碼塊,嵌入任何java代碼 String s = "abcdefg"; s = s.toUpperCase(); out.print(s); %> <%-- JSP註釋 --%> </body> </html>
/* * Generated by the Jasper component of Apache Tomcat * Version: Apache Tomcat/7.0.42 * Generated at: 2016-09-03 12:18:11 UTC * Note: The last modified time of this file was set to * the last modified time of the source file after * generation to assist with modification tracking. */ package org.apache.jsp; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; public final class demo1_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent { // JSP聲明 定義成員變量、成員方法 、內部類 public static void m(){} class A {} private static final javax.servlet.jsp.JspFactory _jspxFactory = javax.servlet.jsp.JspFactory.getDefaultFactory(); private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants; private javax.el.ExpressionFactory _el_expressionfactory; private org.apache.tomcat.InstanceManager _jsp_instancemanager; public java.util.Map<java.lang.String,java.lang.Long> getDependants() { return _jspx_dependants; } public void _jspInit() { _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory(); _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig()); } public void _jspDestroy() { } public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { final javax.servlet.jsp.PageContext pageContext; javax.servlet.http.HttpSession session = null; final javax.servlet.ServletContext application; final javax.servlet.ServletConfig config; javax.servlet.jsp.JspWriter out = null; final java.lang.Object page = this; javax.servlet.jsp.JspWriter _jspx_out = null; javax.servlet.jsp.PageContext _jspx_page_context = null; try { response.setContentType("text/html; charset=UTF-8"); pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out; out.write("\r\n"); out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n"); out.write("<html>\r\n"); out.write("<head>\r\n"); out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n"); out.write("<title>Insert title here</title>\r\n"); out.write("</head>\r\n"); out.write("<body>\r\n"); out.write("<h1>JSP腳本元素</h1>\r\n"); out.write("\r\n"); out.write("\r\n"); out.write("<!-- 表達式 等價於 會被翻譯爲 out.print -->\r\n"); out.print("abcd" ); out.write("\r\n"); out.write("\r\n"); // JSP 腳本代碼塊,嵌入任何java代碼 String s = "abcdefg"; s = s.toUpperCase(); out.print(s); out.write("\r\n"); out.write("</body>\r\n"); out.write("</html>"); } catch (java.lang.Throwable t) { if (!(t instanceof javax.servlet.jsp.SkipPageException)){ out = _jspx_out; if (out != null && out.getBufferSize() != 0) try { out.clearBuffer(); } catch (java.io.IOException e) {} if (_jspx_page_context != null) _jspx_page_context.handlePageException(t); else throw new ServletException(t); } } finally { _jspxFactory.releasePageContext(_jspx_page_context); } } }
語法:<%@ 指令名稱 屬性=值 屬性=值 ... %>
page指令
page指令用來定義JSP文件的全局屬性 <%@ page 屬性=值 %>
在JSP頁面中,只有import能夠出現屢次,其餘屬性都只能出現一次
1.language 只能爲java
2.extends 表示JSP翻譯後的Servlet所繼承的父類,這個屬性通常不設置,由於服務器內部默認使jsp繼承HttpJspBase類;若是非要設置,繼承類必須是Servlet實現類
3.session 定義JSP中是否能夠直接使用Session隱含對象,默認爲true
若是屬性設置爲true,在JSP翻譯Servlet時,生成如下兩句代碼:
HttpSession session = null;
session = pageContext.getSession();
* 若是jsp中想使用HttpSession對象,使用session屬性默認值true
4.import 完成 JSP翻譯後 Servlet 的導包
jsp在翻譯爲Servlet時,默認導入三個包:
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
jre默認導入 java.lang
* 在jsp中若是使用類 不屬於以上四個包,就須要導包
5.buffer和autoFlush 設置 out隱含對象屬性
buffer 設置緩衝區大小
autoFlush 設置當緩衝區滿後,自動刷新
6.isELIgnored 設置JSP是否執行EL表達式
isELIgnored="false" 不忽略---執行解析
isELIgnored="true" 忽略 ---- 不解析
* 通常就是默認值false
7.經過contentType和pageEncoding 設置 JSP頁面編碼
pageEncoding 是 JSP文件源代碼在硬盤上編碼集,若是設置支持中文的編碼集,那麼服務器就能正確讀取jsp中的中文,並將翻譯好的中文字符讀取進內存(注意內存中保存的不是字節)
contentType 在Servlet生成HTML.傳遞給瀏覽器時採用編碼
* Java內存中,是沒有編碼集這一說的,存的都是字符
* 這兩個屬性設置成支持中文的編碼集便可,互相之間不打架的
pageEncoding和contentType區別:
8.經過errorPage和isErrorPage 控制 JSP頁面發生錯誤時跳轉
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%--發生錯誤,想讓用戶看到友好頁面 error.jsp--%> <%@ page errorPage="/demo4/error.jsp" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <!-- 製做錯誤 --> <% int d = 1/0; %> </body> </html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%--當設置了當前頁面是錯誤頁面,則能夠得到內置對象exception,從而得到錯誤信息 --%>
<%@page isErrorPage="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 錯誤友好信息頁面 -->
<h4>對不起,服務器正在升級,請稍後訪問!</h4>
<h5>錯誤緣由:<%=exception.getMessage() %></h5>
</body>
</html>
關於錯誤頁面配置,開發中比較經常使用的是在web.xml中配置<error-page>,一次配置便可.
<error-page> <error-code>500</error-code> <location>/demo5/500.jsp</location> </error-page> <error-page> <error-code>404</error-code> <location>/demo5/404.jsp</location> </error-page>
include指令
用來靜態包含頁面 ----- 將頁面公共部分提取出來,經過include完成頁面佈局。
語法:<%@ include file="文件路徑" %>
include包含的是目標頁面的整個內容,因此被包含頁面,不須要是一個完整HTML,只要編寫HTML片斷就能夠了。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <!-- 主頁面 --> <!-- 經過 include 包含 logo.jsp --> <%@ include file="/demo6/logo.jsp" %> <h1>主頁面其它內容</h1> <%--包含頁面必須存在的--%> <%@ include file="/demo6/footer.jsp" %> </body> </html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <h1>這是系統LOGO</h1>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% String s = "computer@mail.ustc.edu.cn"; %> <%=s %>
taglib指令
用來在jsp頁面引用標籤庫文件
* 定義標籤做用爲了簡化 jsp頁面開發
* 經過taglib 指令引入 jstl標籤庫,語法: <%@ taglib uri="" prefix="" %>
uri ---- 定義標籤 惟一命名空間
prefixt ---- 命名空間前綴
引用jstl時,在導入的jstl.jar中 META-INF/c.tld
<short-name>c</short-name> -------- 就是prefix屬性
<uri>http://java.sun.com/jsp/jstl/core</uri> ----- 就是uri屬性
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%--經過 taglib 指令 引用jstl ,必須導入jstl 的 jar包--%> <%--在 javaee 5 libraries 存在 jstl-1.2.jar--%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <% request.setAttribute("a",10); %> <c:if test="${requestScope.a>8}"> <h1>a的值 大於8</h1> </c:if> </body> </html>
JSP標籤也稱之爲Jsp Action (JSP動做) 元素,它用於在Jsp頁面中提供業務邏輯功能,避免在JSP頁面中直接編寫java代碼,形成jsp頁面難以維護。
注意,這些標籤是默認存在的,不須要引入Jar包。
<jsp:include>
效果等價於request.getRequestDispatcher().include,原理是動態包含,區別於<%@ include file="文件路徑" %>的靜態包含
<jsp:forward>
<jsp:forward page="/demo11/b.jsp"></jsp:forward> 等價於 request.getRequestDispatcher("/demo11/b.jsp").forward(request,response);
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <h1>Hello A</h1> <% // 看不到Hello A,由於在跳轉以前,會清空response 緩衝區 // request.getRequestDispatcher("/demo11/b.jsp").forward(request,response); %> <% request.setAttribute("name", "lichunchun"); %> <jsp:forward page="/demo11/b.jsp"> <jsp:param value="ustc" name="school"/> </jsp:forward> </body> </html>
注:<jsp:forward>以後的代碼不會被執行
<jsp:param >
綁定在<jsp:forward>中能夠用來傳遞參數.
<jsp:forward page="/demo11/b.jsp">
<jsp:param value="ustc" name="school"/>
</jsp:forward>
<jsp:useBean>
<jsp:useBean>標籤用來在jsp頁面中建立一個Bean實例
<jsp:setProperty>
設置bean的屬性
<jsp:getProperty>
獲取bean的屬性
<%@ page language="java" pageEncoding="gb2312"%> <jsp:useBean id="user" scope="page" class="com.jsp.test.TestBean"/> <jsp:setProperty name="user" property="*"/> 或者用如下,param能夠不填寫,其中param對應的是提交頁面的表單name <jsp:setProperty property="userName" name="user" param="userName"/> <jsp:setProperty property="password" name="user" param="password"/> <jsp:setProperty property="age" name="user" param="age"/> <html> <body> 註冊成功:<br> <hr> 使用Bean的屬性方法<br> 用戶名: <%=user.getUserName()%><br> 密碼: <%=user.getPassword()%><br> 年齡: <%=user.getAge()%><br> <hr> 使用getProperty<br> 用戶名:<jsp:getProperty name="user" property="userName"/><br> 密碼: <jsp:getProperty name="user" property="password"/><br> 年齡: <jsp:getProperty name="user" property="age"/> 客戶端名稱:<%=request.getRemoteAddr() %> </body> </html>
JSP編譯爲Servlet代碼時,有些對象是默認已經建立好的,這類對象能夠直接在jsp中使用,稱之爲九大內置對象.
page對象
page 表明當前jsp生成的Servlet對象
* page 是 Object類型,只能使用Object中方法 ---- 這個對象在開發中不建議使用
* 能夠將page強制轉換成HttpServlet對象
<%
HttpServlet httpServlet = (HttpServlet)page;
out.print(httpServlet.getServletContext().getRealPath("/"));
%>
JSP四種數據域對象
前面咱們提到過,Servlet將數據存儲於Request,ServletContext,Session三種域.
JSP在以上基礎擴充了page對象,共有四種域對象(request,application,session,page),其中application是ServletContext的實現.
* page數據範圍存放數據,只在當前jsp內有效
* 向page 範圍保存數據,必須經過 pageContext對象 setAttribute方法
*pageContext還能夠獲取其餘八個隱式對象.
out對象
out 功能向瀏覽器輸出信息,是JspWriter類型,內部使用PrintWriter實現,擁有獨立緩衝區。
out建立:out對象經過pageContext對象得到,而在建立pageContext對象時,需指定out緩衝區大小以及是否自動flush
* 經過 page指令 buffer autoFlush 設置out 緩存區大小 以及是否自動 flush,默認的緩衝區是8kb.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page isErrorPage="true" %> <%--經過 buffer和autoFlush 設置out 對象緩衝區--%> <%--<%@page buffer="1kb" autoFlush="false" %>--%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <h1>JSP 九個內置對象</h1> <% // 非要使用page對象 HttpServlet httpServlet = (HttpServlet)page; out.print(httpServlet.getServletContext().getRealPath("/")); %> <hr/> <% // 向四種數據範圍保存數據 request.setAttribute("name","request"); session.setAttribute("name","session"); application.setAttribute("name","application"); // 向page 範圍保存數據,必須經過 pageContext對象 pageContext.setAttribute("name","page"); %> <%=request.getAttribute("name") %> <%=session.getAttribute("name") %> <%=application.getAttribute("name") %> <%=pageContext.getAttribute("name") %> <% // 想在四個數據範圍查詢 指定名稱數據 // 順序按照 page -- request -- session -- application Object value = pageContext.findAttribute("name"); %> <h3>查找name屬性 :<%=value %></h3> <h1>經過EL 取得數據</h1> ${sessionScope.name } <!-- 若是直接寫name 默認會調用 pageContext.findAttribute --> ${name } </body> </html>
觀察JSP編譯的Servlet文件能夠查看這些隱式對象.
exception對象
exception對象是java.lang.Trowable類的實例 (使用前須要在jsp頁面設置page指令 isErrorPage=「true」)
exception對象用來處理JSP文件在執行時全部發生的錯誤和異常
exception對象能夠和page指令一塊兒使用,經過指定某一個頁面爲錯誤處理頁面,對錯誤進行處理
<%@ page isErrorPage="true"%>的頁面內使用。(最好仍是用第二種配置web.xml的方式)
EL語言屬於小團隊開發,後來在Servlet2.4以後被併入了官方規範之中,目的是爲了簡化JSP代碼開發.
主要功能:
獲取JSP四個範圍中保存的數據
${pageScope.屬性名稱}
${requestScope.屬性名稱}
${sessionScope.屬性名稱}
${applicationScope.屬性名}
若是查找屬性不存在,返回是一個 "" 空串,而不是null
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <!-- 經過el 得到四個數據範圍 數據 page request session application--> <% pageContext.setAttribute("city","合肥"); request.setAttribute("name","李春春"); session.setAttribute("school","中國科學技術大學"); application.setAttribute("pnum",100); %> ${pageScope.city } ${requestScope.name } ${sessionScope.school } ${applicationScope.pnum } <h1>省略指定範圍, 默認調用pageContext.findAttribute() 在四個範圍依次查找</h1> ${name } ${city } <h1>EL找不到數據返回""空串、傳統表達式方式找不到數據返回null</h1> <h3>abc: <%=request.getAttribute("abc") %></h3> <h3>abc: ${abc }</h3> </body> </html>
獲取JavaBean屬性,數組,Collection,Map等數據集合
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@page import="ustc.lichunchun.domain.Person"%> <%@page import="ustc.lichunchun.domain.City"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <!-- 經過EL 得到 存放在四個範圍內的 java對象類型 --> <% Person person = new Person(); person.setName("李春春"); person.setAge(24); City city = new City(); city.setName("合肥"); person.setCity(city); pageContext.setAttribute("person", person); %> ${pageScope.person.name } <!-- 上面寫法等價於 pageContext.getAttribute("person").getName() --> ${pageScope.person.age } ${pageScope.person["age"] } ${pageScope["person"]["age"] } <!-- 得到person的city對象名稱 --> ${pageScope.person.city.name } <!-- pageContext.getAttribute("person").getCity().getName() --> ${pageScope["person"]["city"]["name"] } </body> </html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@page import="java.util.List"%> <%@page import="java.util.ArrayList"%> <%@page import="java.util.Map"%> <%@page import="java.util.HashMap"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <!-- 經過EL 取得 List 或者 Map中數據 --> <% List list = new ArrayList(); list.add("abc"); list.add("bcd"); list.add("efg"); // 將list 保存page範圍 pageContext.setAttribute("list",list); %> ${pageScope.list } 取得list的第二個元素 :${pageScope.list[1] }<br/> <% Map map = new HashMap(); map.put("aaa","111"); map.put("bbb","222"); pageContext.setAttribute("map",map); %> 取得 map 中 bbb對應 value : ${pageScope.map.bbb }、${pageScope.map["bbb"] }<br/> </body> </html>
上述代碼獲取數組,List,Map時,可使用.或者[]獲取
. 和 [ ] 有什麼區別 ?
答案:. 和 [ ] 均可以用來取得EL 屬性值,.能夠實現的功能[ ] 也均可以!
例如: ${pageScope.user.name} 也能夠寫爲 ${pageScope.user["name"]}
[ ] 可使用特殊標識信息,可是. 不能夠
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@page import="java.util.List"%> <%@page import="java.util.ArrayList"%> <%@page import="java.util.Map"%> <%@page import="java.util.HashMap"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <!-- 經過EL 取得 List 或者 Map中數據 --> <% List list = new ArrayList(); list.add("abc"); list.add("bcd"); list.add("efg"); // 將list 保存page範圍 pageContext.setAttribute("list",list); %> ${pageScope.list } 取得list的第二個元素 :${pageScope.list[1] }<br/> <% Map map = new HashMap(); map.put("aaa","111"); map.put("bbb","222"); pageContext.setAttribute("map",map); %> 取得 map 中 bbb對應 value : ${pageScope.map.bbb }、${pageScope.map["bbb"] }<br/> <h1>. 和 [] 區別</h1> <% pageContext.setAttribute("0","itcast"); pageContext.setAttribute("aa.bb","特殊標識信息"); %> 特殊字符0 屬性值:${pageScope["0"] } <br/> 特殊字符 aa.bb 屬性值 :${pageScope["aa.bb"] } <br/> <% String ds = "aa.bb"; pageContext.setAttribute("s",ds); %> <!-- 在使用[] 進行屬性取值時,要加"" , 若不加"" 則認爲是一個變量 --> 特殊字符 aa.bb 屬性值 :${pageScope[s] }<br/><!-- 特殊標識信息 --> 特殊字符 aa.bb 屬性值 :${pageScope["s"] }<!-- aa.bb --> <!-- 利用el表達式獲取web應用的名稱 --> <a href="${pageContext.request.contextPath }/demo1.jsp">點我</a> </body> </html>
算術,比較,邏輯運算
在EL 執行運算時,運算語句必須寫入 ${ }中
* 在EL 得到屬性值 執行算術運算,自動類型轉換 ---- 執行算術運算時,進行運算參數,必須都是數字
${"a"+"b"} ---- 發生數字格式化錯誤
empty運算符
1) 判斷一個屬性是否存在 , 一般empty運算符都是結合c:if 一塊兒使用
2) 使用empty 判斷List 或者 Map是否爲空 (size==0)
二元表達式:${user!=null?user.name:""} ----- 三元運算符
不能使用保留字存儲屬性,保留字有特殊意義
EL表達式保留關鍵字:
<%@page import="java.util.HashMap"%> <%@page import="java.util.ArrayList"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <h2>EL 執行 運算</h2> <% pageContext.setAttribute("a", "10"); pageContext.setAttribute("b", "20"); pageContext.setAttribute("10", "30"); %> ${a+b }<!-- 30 --> <%--經典錯誤 :${"a"+"b" }--%> ${pageScope.a }<!-- 10 --> ${pageScope["a"] }<!-- 10 --> ${pageScope[a] }<!-- 30 --> ${a }<!-- 10 --> ${"a" }<!-- a --> <h2>empty運算符</h2> ${empty name }<!-- 若是四個數據範圍都沒有name屬性 返回true --> <c:if test="${empty name }"> <h3>根本不存在 name數據</h3> </c:if> <!-- 判斷list 得到 map是否爲空 --> <% pageContext.setAttribute("list", new ArrayList()); pageContext.setAttribute("map", new HashMap()); %> ${empty list } ${empty map } <h2>二元表達式</h2> ${(empty map)?"map中沒有任何元素":"map不爲空" } <% // 不能使用保留字 存儲屬性,保留字有特殊意義 pageContext.setAttribute("empty","111"); %> <%--${pageContext["empty"] }--%> </body> </html>
內置11個對象(web開發經常使用對象)
項目開發中,JSP開發基本都會約定引入JSTL標籤庫(Java Standard Tag Liberary),統一規範,簡化代碼開發.
下載jstl.jar和standard.jar,經過taglib指令引入jstl標籤庫對應的uri,也能夠在web.xml中直接進行配置
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <jsp-config> <taglib> <taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri> <taglib-location>/WEB-INF/fmt.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/fmt-rt</taglib-uri> <taglib-location>/WEB-INF/fmt-rt.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/core</taglib-uri> <taglib-location>/WEB-INF/c.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/core-rt</taglib-uri> <taglib-location>/WEB-INF/c-rt.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/sql</taglib-uri> <taglib-location>/WEB-INF/sql.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/sql-rt</taglib-uri> <taglib-location>/WEB-INF/sql-rt.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/x</taglib-uri> <taglib-location>/WEB-INF/x.tld</taglib-location> </taglib> <taglib> <taglib-uri>http://java.sun.com/jstl/x-rt</taglib-uri> <taglib-location>/WEB-INF/x-rt.tld</taglib-location> </taglib> </jsp-config> </web-app>
JSTL由五種主要標籤組成:
具體的語法參考
過濾器能夠對請求和響應作出攔截操做.
配置多個filter:
1.繼承filter類,實現init,doFilter,destroy三個類方法.
//導入必需的 java 庫 import javax.servlet.*; import java.util.*; //實現 Filter 類 public class LogFilter implements Filter { public void init(FilterConfig config) throws ServletException { // 獲取初始化參數 String site = config.getInitParameter("Site"); // 輸出初始化參數 System.out.println("網站名稱: " + site); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException { // 輸出站點名稱 System.out.println("站點網址:http://www.runoob.com"); // 把請求傳回過濾鏈 chain.doFilter(request,response); } public void destroy( ){ /* 在 Filter 實例被 Web 容器從服務移除以前調用 */ } }
2.在web.xml進行配置
<filter> <filter-name>LogFilter</filter-name> <filter-class>com.runoob.test.LogFilter</filter-class> <init-param> <param-name>test-param</param-name> <param-value>Initialization Paramter</param-value> </init-param> </filter> <filter> <filter-name>AuthenFilter</filter-name> <filter-class>com.runoob.test.AuthenFilter</filter-class> <init-param> <param-name>test-param</param-name> <param-value>Initialization Paramter</param-value> </init-param> </filter> <filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>AuthenFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
監聽器顧名思義,用來觀察對象的狀態.在Servlet規範中提供了8個監聽器接口.
建立、銷燬監聽器3個
ServletContextListener:監聽ServletContext的建立和銷燬的監聽器
HttpSessionListener:監聽HttpSession的建立和銷燬的監聽器
ServletRequestListener:監聽ServletRequest的建立和銷燬的監聽器
屬性變化監聽器3個
ServletContextAttributeListener:監聽放到應用範圍中的數據變化(新添加、修改的、刪除的)
HttpSessionAttributeListener:(統計登陸用戶列表)
ServletRequestAttributeListener
感知型監聽器2個
HttpSessionBindingListener:誰實現這個接口,就能感知本身什麼時候被HttpSession綁定和解綁了
HttpSessionActivationListener:誰實現這個接口,就能感知本身什麼時候隨着HttpSession對象鈍化和激活
web.xml中配置監聽器
<listener> <listener-class> com.journaldev.listener.AppContextListener </listener-class> </listener>
注意,這裏代碼是搬運蒼狼大神的博文,用於模仿學習.
文件的上傳下載是使用流來進行傳輸,java web通常使用的是apache的file-upload組件.
1.引入commons-fileupload.jar和commons-io.jar
2.表單中須要<input>的type標爲file,
enctype標爲multipart/form-data,
method標爲post
3. 編寫Servlet
package me.gacl.web.controller; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; public class UploadHandleServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //獲得上傳文件的保存目錄,將上傳的文件存放於WEB-INF目錄下,不容許外界直接訪問,保證上傳文件的安全 String savePath = this.getServletContext().getRealPath("/WEB-INF/upload"); File file = new File(savePath); //判斷上傳文件的保存目錄是否存在 if (!file.exists() && !file.isDirectory()) { System.out.println(savePath+"目錄不存在,須要建立"); //建立目錄 file.mkdir(); } //消息提示 String message = ""; try{ //使用Apache文件上傳組件處理文件上傳步驟: //一、建立一個DiskFileItemFactory工廠 DiskFileItemFactory factory = new DiskFileItemFactory(); //二、建立一個文件上傳解析器 ServletFileUpload upload = new ServletFileUpload(factory); //解決上傳文件名的中文亂碼 upload.setHeaderEncoding("UTF-8"); //三、判斷提交上來的數據是不是上傳表單的數據 if(!ServletFileUpload.isMultipartContent(request)){ //按照傳統方式獲取數據 return; } //四、使用ServletFileUpload解析器解析上傳數據,解析結果返回的是一個List<FileItem>集合,每個FileItem對應一個Form表單的輸入項 List<FileItem> list = upload.parseRequest(request); for(FileItem item : list){ //若是fileitem中封裝的是普通輸入項的數據 if(item.isFormField()){ String name = item.getFieldName(); //解決普通輸入項的數據的中文亂碼問題 String value = item.getString("UTF-8"); //value = new String(value.getBytes("iso8859-1"),"UTF-8"); System.out.println(name + "=" + value); }else{//若是fileitem中封裝的是上傳文件 //獲得上傳的文件名稱, String filename = item.getName(); System.out.println(filename); if(filename==null || filename.trim().equals("")){ continue; } //注意:不一樣的瀏覽器提交的文件名是不同的,有些瀏覽器提交上來的文件名是帶有路徑的,如: c:\a\b\1.txt,而有些只是單純的文件名,如:1.txt //處理獲取到的上傳文件的文件名的路徑部分,只保留文件名部分 filename = filename.substring(filename.lastIndexOf("\\")+1); //獲取item中的上傳文件的輸入流 InputStream in = item.getInputStream(); //建立一個文件輸出流 FileOutputStream out = new FileOutputStream(savePath + "\\" + filename); //建立一個緩衝區 byte buffer[] = new byte[1024]; //判斷輸入流中的數據是否已經讀完的標識 int len = 0; //循環將輸入流讀入到緩衝區當中,(len=in.read(buffer))>0就表示in裏面還有數據 while((len=in.read(buffer))>0){ //使用FileOutputStream輸出流將緩衝區的數據寫入到指定的目錄(savePath + "\\" + filename)當中 out.write(buffer, 0, len); } //關閉輸入流 in.close(); //關閉輸出流 out.close(); //刪除處理文件上傳時生成的臨時文件 item.delete(); message = "文件上傳成功!"; } } }catch (Exception e) { message= "文件上傳失敗!"; e.printStackTrace(); } request.setAttribute("message",message); request.getRequestDispatcher("/message.jsp").forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
4.web.xml註冊Servlet
<servlet> <servlet-name>UploadHandleServlet</servlet-name> <servlet-class>me.gacl.web.controller.UploadHandleServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>UploadHandleServlet</servlet-name> <url-pattern>/servlet/UploadHandleServlet</url-pattern> </servlet-mapping>
5.改進
一、爲保證服務器安全,上傳文件應該放在外界沒法直接訪問的目錄下,好比放於WEB-INF目錄下。
二、爲防止文件覆蓋的現象發生,要爲上傳文件產生一個惟一的文件名。
三、爲防止一個目錄下面出現太多文件,要使用hash算法打散存儲。
四、要限制上傳文件的最大值。
五、要限制上傳文件的類型,在收到上傳文件名時,判斷後綴名是否合法。
package me.gacl.web.controller; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.UUID; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadBase; import org.apache.commons.fileupload.ProgressListener; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; /** * @ClassName: UploadHandleServlet * @Description: TODO(這裏用一句話描述這個類的做用) * @author: 孤傲蒼狼 * @date: 2015-1-3 下午11:35:50 * */ public class UploadHandleServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //獲得上傳文件的保存目錄,將上傳的文件存放於WEB-INF目錄下,不容許外界直接訪問,保證上傳文件的安全 String savePath = this.getServletContext().getRealPath("/WEB-INF/upload"); //上傳時生成的臨時文件保存目錄 String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp"); File tmpFile = new File(tempPath); if (!tmpFile.exists()) { //建立臨時目錄 tmpFile.mkdir(); } //消息提示 String message = ""; try{ //使用Apache文件上傳組件處理文件上傳步驟: //一、建立一個DiskFileItemFactory工廠 DiskFileItemFactory factory = new DiskFileItemFactory(); //設置工廠的緩衝區的大小,當上傳的文件大小超過緩衝區的大小時,就會生成一個臨時文件存放到指定的臨時目錄當中。 factory.setSizeThreshold(1024*100);//設置緩衝區的大小爲100KB,若是不指定,那麼緩衝區的大小默認是10KB //設置上傳時生成的臨時文件的保存目錄 factory.setRepository(tmpFile); //二、建立一個文件上傳解析器 ServletFileUpload upload = new ServletFileUpload(factory); //監聽文件上傳進度 upload.setProgressListener(new ProgressListener(){ public void update(long pBytesRead, long pContentLength, int arg2) { System.out.println("文件大小爲:" + pContentLength + ",當前已處理:" + pBytesRead); /** * 文件大小爲:14608,當前已處理:4096 文件大小爲:14608,當前已處理:7367 文件大小爲:14608,當前已處理:11419 文件大小爲:14608,當前已處理:14608 */ } }); //解決上傳文件名的中文亂碼 upload.setHeaderEncoding("UTF-8"); //三、判斷提交上來的數據是不是上傳表單的數據 if(!ServletFileUpload.isMultipartContent(request)){ //按照傳統方式獲取數據 return; } //設置上傳單個文件的大小的最大值,目前是設置爲1024*1024字節,也就是1MB upload.setFileSizeMax(1024*1024); //設置上傳文件總量的最大值,最大值=同時上傳的多個文件的大小的最大值的和,目前設置爲10MB upload.setSizeMax(1024*1024*10); //四、使用ServletFileUpload解析器解析上傳數據,解析結果返回的是一個List<FileItem>集合,每個FileItem對應一個Form表單的輸入項 List<FileItem> list = upload.parseRequest(request); for(FileItem item : list){ //若是fileitem中封裝的是普通輸入項的數據 if(item.isFormField()){ String name = item.getFieldName(); //解決普通輸入項的數據的中文亂碼問題 String value = item.getString("UTF-8"); //value = new String(value.getBytes("iso8859-1"),"UTF-8"); System.out.println(name + "=" + value); }else{//若是fileitem中封裝的是上傳文件 //獲得上傳的文件名稱, String filename = item.getName(); System.out.println(filename); if(filename==null || filename.trim().equals("")){ continue; } //注意:不一樣的瀏覽器提交的文件名是不同的,有些瀏覽器提交上來的文件名是帶有路徑的,如: c:\a\b\1.txt,而有些只是單純的文件名,如:1.txt //處理獲取到的上傳文件的文件名的路徑部分,只保留文件名部分 filename = filename.substring(filename.lastIndexOf("\\")+1); //獲得上傳文件的擴展名 String fileExtName = filename.substring(filename.lastIndexOf(".")+1); //若是須要限制上傳的文件類型,那麼能夠經過文件的擴展名來判斷上傳的文件類型是否合法 System.out.println("上傳的文件的擴展名是:"+fileExtName); //獲取item中的上傳文件的輸入流 InputStream in = item.getInputStream(); //獲得文件保存的名稱 String saveFilename = makeFileName(filename); //獲得文件的保存目錄 String realSavePath = makePath(saveFilename, savePath); //建立一個文件輸出流 FileOutputStream out = new FileOutputStream(realSavePath + "\\" + saveFilename); //建立一個緩衝區 byte buffer[] = new byte[1024]; //判斷輸入流中的數據是否已經讀完的標識 int len = 0; //循環將輸入流讀入到緩衝區當中,(len=in.read(buffer))>0就表示in裏面還有數據 while((len=in.read(buffer))>0){ //使用FileOutputStream輸出流將緩衝區的數據寫入到指定的目錄(savePath + "\\" + filename)當中 out.write(buffer, 0, len); } //關閉輸入流 in.close(); //關閉輸出流 out.close(); //刪除處理文件上傳時生成的臨時文件 //item.delete(); message = "文件上傳成功!"; } } }catch (FileUploadBase.FileSizeLimitExceededException e) { e.printStackTrace(); request.setAttribute("message", "單個文件超出最大值!!!"); request.getRequestDispatcher("/message.jsp").forward(request, response); return; }catch (FileUploadBase.SizeLimitExceededException e) { e.printStackTrace(); request.setAttribute("message", "上傳文件的總的大小超出限制的最大值!!!"); request.getRequestDispatcher("/message.jsp").forward(request, response); return; }catch (Exception e) { message= "文件上傳失敗!"; e.printStackTrace(); } request.setAttribute("message",message); request.getRequestDispatcher("/message.jsp").forward(request, response); } /** * @Method: makeFileName * @Description: 生成上傳文件的文件名,文件名以:uuid+"_"+文件的原始名稱 * @Anthor:孤傲蒼狼 * @param filename 文件的原始名稱 * @return uuid+"_"+文件的原始名稱 */ private String makeFileName(String filename){ //2.jpg //爲防止文件覆蓋的現象發生,要爲上傳文件產生一個惟一的文件名 return UUID.randomUUID().toString() + "_" + filename; } /** * 爲防止一個目錄下面出現太多文件,要使用hash算法打散存儲 * @Method: makePath * @Description: * @Anthor:孤傲蒼狼 * * @param filename 文件名,要根據文件名生成存儲目錄 * @param savePath 文件存儲路徑 * @return 新的存儲目錄 */ private String makePath(String filename,String savePath){ //獲得文件名的hashCode的值,獲得的就是filename這個字符串對象在內存中的地址 int hashcode = filename.hashCode(); int dir1 = hashcode&0xf; //0--15 int dir2 = (hashcode&0xf0)>>4; //0-15 //構造新的保存目錄 String dir = savePath + "\\" + dir1 + "\\" + dir2; //upload\2\3 upload\3\5 //File既能夠表明文件也能夠表明目錄 File file = new File(dir); //若是目錄不存在 if(!file.exists()){ //建立目錄 file.mkdirs(); } return dir; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
1.下載文件頁面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE HTML> <html> <head> <title>下載文件顯示頁面</title> </head> <body> <!-- 遍歷Map集合 --> <c:forEach var="me" items="${fileNameMap}"> <c:url value="/servlet/DownLoadServlet" var="downurl"> <c:param name="filename" value="${me.key}"></c:param> </c:url> ${me.value}<a href="${downurl}">下載</a> <br/> </c:forEach> </body> </html>
2.執行下載操做的Servlet
package me.gacl.web.controller; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class DownLoadServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //獲得要下載的文件名 String fileName = request.getParameter("filename"); //23239283-92489-阿凡達.avi fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8"); //上傳的文件都是保存在/WEB-INF/upload目錄下的子目錄當中 String fileSaveRootPath=this.getServletContext().getRealPath("/WEB-INF/upload"); //經過文件名找出文件的所在目錄 String path = findFileSavePathByFileName(fileName,fileSaveRootPath); //獲得要下載的文件 File file = new File(path + "\\" + fileName); //若是文件不存在 if(!file.exists()){ request.setAttribute("message", "您要下載的資源已被刪除!!"); request.getRequestDispatcher("/message.jsp").forward(request, response); return; } //處理文件名 String realname = fileName.substring(fileName.indexOf("_")+1); //設置響應頭,控制瀏覽器下載該文件 response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8")); //讀取要下載的文件,保存到文件輸入流 FileInputStream in = new FileInputStream(path + "\\" + fileName); //建立輸出流 OutputStream out = response.getOutputStream(); //建立緩衝區 byte buffer[] = new byte[1024]; int len = 0; //循環將輸入流中的內容讀取到緩衝區當中 while((len=in.read(buffer))>0){ //輸出緩衝區的內容到瀏覽器,實現文件下載 out.write(buffer, 0, len); } //關閉文件輸入流 in.close(); //關閉輸出流 out.close(); } /** * @Method: findFileSavePathByFileName * @Description: 經過文件名和存儲上傳文件根目錄找出要下載的文件的所在路徑 * @Anthor:孤傲蒼狼 * @param filename 要下載的文件名 * @param saveRootPath 上傳文件保存的根目錄,也就是/WEB-INF/upload目錄 * @return 要下載的文件的存儲目錄 */ public String findFileSavePathByFileName(String filename,String saveRootPath){ int hashcode = filename.hashCode(); int dir1 = hashcode&0xf; //0--15 int dir2 = (hashcode&0xf0)>>4; //0-15 String dir = saveRootPath + "\\" + dir1 + "\\" + dir2; //upload\2\3 upload\3\5 File file = new File(dir); if(!file.exists()){ //建立目錄 file.mkdirs(); } return dir; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
3.web.xml配置
<servlet> <servlet-name>DownLoadServlet</servlet-name> <servlet-class>me.gacl.web.controller.DownLoadServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DownLoadServlet</servlet-name> <url-pattern>/servlet/DownLoadServlet</url-pattern> </servlet-mapping>