上一篇講了ServletContent、ServletCOnfig、HTTPSession、request、response幾個對象的生命周期、作用范圍和一些用法。今天通過一個小項目運用這些知識。簡單的注冊登錄,文件的上傳和下載。
大致思路:
注冊登錄和文件上傳和下載
注冊成功后跳轉到登錄界面,登陸成功后跳轉到主界面,主界面有上傳和下載功能。
本次注冊登錄不用數據庫,注冊成功后放到一個map集合中,key值為賬號,value值為密碼。
注冊成功把當前用戶信息放入map集合並且使用ServletContent對象保存。
一:准備階段
先在WebContent目錄下創建幾個html頁面。
注冊頁面 register.html :
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>注冊</title>
</head>
<body>
<br><br><br>
<!-- action代表form表單提交給那個servlet處理 method指定提交方式:get代表把參數拼接在url后面 post代表把參數放在http請求體中 -->
<form action="register" method="post">
<div align="center">
<span>賬號</span><input type="text" name="username">
<br>
<span>密碼</span><input type="password" name="pw">
<div align="center">
<input type="submit" value="注冊"/>
</div>
</div>
</form>
</body>
</html>
登錄界面login.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登錄</title>
</head>
<body>
<br><br><br>
<form action="login" method="post">
<div align="center">
<span>賬號</span><input type="text" name="username">
<br>
<span>密碼</span><input type="password" name="pw">
<br>
<div align="center">
<input type="submit" value="登陸"/>
</div>
</div>
</form>
</body>
</html>
注冊失敗提示頁面registerError.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>注冊錯誤提示</title>
</head>
<body>
<div align="center">
<h1>賬號重復</h1>
<br>
<a href="register.html">跳轉到注冊界面</a>
</div>
</body>
</html>
登錄失敗提示頁面loginError.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登錄錯誤提示</title>
</head>
<body>
<div align="center">
<h1>賬號密碼錯誤</h1>
<br>
<a href="login.html">跳轉到登錄界面</a>
</div>
</body>
</html>
功能頁面success.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>主界面</title>
</head>
<body>
<div align="center">
<!-- enctype這個屬性管理的是表單的MIME編碼。共有三個值可選: 1、application/x-www-form-urlencoded 2、multipart/form-data 3、text/plain 文件上傳值需要指定multipart/form-data -->
<h1>文件上傳</h1>
<form action="upload" method="post" enctype="multipart/form-data">
<input type="text" name="username" />
<br>
<input type="file" name="MyImage" />
<br>
<input type="submit" value="上傳" />
</form>
</div>
<br><br>
<div align="center">
<!-- 指定幾個文件提供下載
等后面學了jsp,el表達式和jstl和新標簽庫可以拓展項目
通過ServletContent對象調用getResourcePaths("/download")可以獲得
該目錄下的所有內容。【/download/文件名】,然后利用這幾個技術動態的資源列舉出來
供下載。 -->
<h1>文件下載</h1>
<a href="download?fileName=1.jpg">1.jpg</a>
<br>
<a href="download?fileName=movie.wmv">moviemwmv</a>
</div>
</body>
</html>
文件長傳和下載在webContent目錄下創建兩個文件夾upload和download,在download文件夾下放幾個資源供下載。
准備階段完成后就可以開始着手寫servlet。
二:編寫注冊和登錄的servlet
注冊和登錄的servlet代碼【注意:需要把<url-pattern>...</url-pattern>首字母改成小寫和html頁面對應】:
package com.briup.servlet.function; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class Register * 注冊 */
public class Register extends HttpServlet { private static final long serialVersionUID = 1L; public Register() { super(); } @Override public void init() throws ServletException { //創建一個Map集合,然后通過ServletContent對象存進去
Map<String,String> map = new HashMap<String,String>(); this.getServletContext().setAttribute("map", map); } @SuppressWarnings("unchecked") protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //獲取注冊的賬號和密碼
String name = request.getParameter("username"); String pw = request.getParameter("pw"); //和map集合對比【不對賬號密碼格式做驗證】
Map<String,String> map = (Map<String, String>) this.getServletContext().getAttribute("map"); if(map.isEmpty()) { map.put(name, pw); //客戶端重定向到登錄界面
response.sendRedirect("login.html"); } else { Set<String> key = map.keySet(); //注冊只需要對比賬號,密碼重復無所謂
for(String s : key) { if(s.equals(name)) { //重定向到注冊錯誤頁面
response.sendRedirect("registerError.html"); } else { map.put(name, pw); //客戶端重定向到登錄界面
response.sendRedirect("login.html"); } } } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
package com.briup.servlet.function; import java.io.IOException; import java.util.Map; import java.util.Set; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class Login * 登錄Servlet */
public class Login extends HttpServlet { private static final long serialVersionUID = 1L; public Login() { super(); } @SuppressWarnings("unchecked") protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //獲取賬號密碼
String name = request.getParameter("username"); String pw = request.getParameter("pw"); //獲取map集合
Map<String,String> map = (Map<String, String>) this.getServletContext().getAttribute("map"); if(map.isEmpty()) { //服務器內部跳轉到錯誤界面
request.getRequestDispatcher("loginError.html").forward(request, response); } else { Set<String> keySet = map.keySet(); for(String s : keySet) { if(s.equals(name) && map.get(s).equals(pw)) { /* * 注意:執行到服務器內部跳轉語句或客服端重定向不會中止函數的執行 * 和return在函數的作用不一樣 * 會繼續執行下去如果跳轉語句和重定向后面還有語句 */
//System.out.println(s + "," + map.get(s)); //response.sendRedirect("success.html");
request.getRequestDispatcher("success.html").forward(request, response); //需要顯式的加上return
return; } } //System.out.println("......"); //如果代碼執行到這里說明賬號或密碼不對 //response.sendRedirect("loginError.html");
request.getRequestDispatcher("loginError.html").forward(request, response); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub
doGet(request, response); } }
三:文件的下載
文件的下載和客戶端重定向有點類似,向瀏覽器發送數據,但是要告訴瀏覽器這是文件下載要保存下來而不是當做html頁面解析。
response有個方法:getWriter():PrintWriter;獲得一個字符輸出流,該輸出流可以向客戶端輸出數據。文件下載是否可以通過其寫到頁面?
傳輸文件一般用字節流,有個類似的方法getOutputStream()可以獲得字節流,通過該字節流向瀏覽器發送數據。
response.setContentType("text/html;charset=utf-8")設置發給瀏覽器的是html頁面【默認】,那么文件下載需要設置為那個?application/x-download。
需要注意的是:我們需要獲取下載文件的絕對路徑,項目會被部署到tomcat安裝目錄下的webapp目錄下,而在src的源代碼則在 "項目名/WEB-INF/classes"下,
使用相對路徑就會出錯。
Dowload.java代碼:
package com.briup.servlet.function; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class Download * 文件下載 */
public class Download extends HttpServlet { private static final long serialVersionUID = 1L; //把下載目錄配置在web.xml中作為參數
private String path; @Override public void init() throws ServletException { path = this.getServletConfig().getInitParameter("path"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 獲得用戶需要下載文件的名字
String fileName = request.getParameter("fileName"); // 下載文件所在目錄的絕對路徑
String realPath = getServletContext().getRealPath(path); // 設置為下載application/x-download
response.setContentType("application/x-download"); // 下載文件時顯示的文件保存名稱
String fileDisplay = "cnblogs_"+fileName; // 中文編碼轉換
fileDisplay = URLEncoder.encode(fileDisplay, "UTF-8"); //設置響應頭部信息
response.addHeader("Content-Disposition", "attachment;filename="+fileDisplay); try { ServletOutputStream out = response.getOutputStream(); File file = new File(realPath,fileName); FileInputStream in = new FileInputStream(file); byte[] b = new byte[1024]; int len = -1; while ((len = in.read(b)) != -1) { out.write(b, 0, len); } out.flush(); in.close(); out.close(); } catch (Exception e) { e.printStackTrace(); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub
doGet(request, response); } }
在web.xml中servlet的配置:
<servlet>
<description></description>
<display-name>Download</display-name>
<servlet-name>Download</servlet-name>
<servlet-class>com.briup.servlet.function.Download</servlet-class>
<init-param>
<param-name>path</param-name>
<param-value>/download</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Download</servlet-name>
<url-pattern>/download</url-pattern>
</servlet-mapping>
有時候會出現中文亂碼,由於沒有約定好。可以通過requst.setEncoding(編碼)來解析瀏覽器發送過來的數據,通過
response.setEnconding(編碼)告訴瀏覽器以那種編碼解析服務器發送過來的數據。如果有多個servlet需要設置編碼,每個都這樣設置會顯得麻煩,有時候會忘記。
這時候過濾器就可以起到很好的作用。
過濾器,顧名思義,就是過濾掉一些東西。在web中過濾器像一個中介一樣在瀏覽器和服務器中間做一些工作,對web管理的所有web資源:例如Jsp, Servlet, 靜態圖片文件或靜態 html 文件等進行攔截,從而實現一些特殊的功能。比如:不登錄就不允許你訪問主界面,實現URL級別的權限訪問控制、過濾敏感詞匯、壓縮響應信息等一些高級功能。
實現javax.servlet.Filter接口的類就是過濾器。和Servlet一樣Filter的創建和銷毀也是由WEB服務器負責。不過與Servlet區別的是,它是1>在應用啟動的時候就進行裝載Filter類(與Servlet的load-on-startup配置效果相同)。2>容器創建好Filter對象實例后,調用init()方法。接着被Web容器保存進應用級的集合容器中去了等待着,用戶訪問資源。3>當用戶訪問的資源正好被Filter的url-pattern攔截時,容器會取出Filter類調用doFilter方法,下次或多次訪問被攔截的資源時,Web容器會直接取出指定Filter對象實例調用doFilter方法(Filter對象常駐留Web容器了)。4>當應用服務被停止或重新裝載了,則會執行Filter的destroy方法,Filter對象銷毀。
這次我們使用Filter做個編碼過濾器,代碼:
package com.briup.servlet.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /** * Servlet Filter implementation class EncodingFilter */
public class EncodingFilter implements Filter { private String encoding; public EncodingFilter() { // TODO Auto-generated constructor stub
} public void destroy() { // TODO Auto-generated method stub
} public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //設置編碼格式【只對post方式有效】;
request.setCharacterEncoding(encoding); response.setCharacterEncoding(encoding); //放行;
chain.doFilter(request, response); } public void init(FilterConfig fConfig) throws ServletException { //我們把編碼設置在web.xml中,如果需要改編碼在配置文件中更改而不需要更改代碼
encoding = fConfig.getInitParameter("encoding"); } }
web.xml配置:
<filter>
<display-name>EncodingFilter</display-name>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.briup.servlet.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<!-- "*"通配符代表攔截所有請求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
運行項目看效果:
四:文件的上傳
網上有許多的上傳組件,common-fileupload組件是apache的一個開源項目之一,用該組件可實現一次上傳一個或多個文件,並可限制文件大小。
該組件需要用到兩個jar包,我用的是commons-fileupload-1.2.2.jar,commons-io-2.0.1.jar。到網上下載這兩個jar包然后放到WEB-INF/lib目錄下【該目錄可以放本項目用到的jar包,會自動構建,和tomcat服務器下的lib目錄相似,不過后者的作用范圍更大,添加進服務器的項目都可以使用里面的jar包】。
源碼:
package com.briup.servlet.function;
import java.io.File;
import java.io.IOException;
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;
/**
* Servlet implementation class Upload
*/
public class Upload extends HttpServlet {
private static final long serialVersionUID = 1L;
private String path;
public Upload() {
super();
}
@Override
public void init() throws ServletException {
path = this.getServletConfig().getInitParameter("path");
}
@SuppressWarnings("unchecked")
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 獲得上傳目錄的絕對路徑
String realpath = this.getServletContext().getRealPath(path);
System.out.println(path);
try {
// 構造一個文件上傳處理對象
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upLoad = new ServletFileUpload(factory);
// 獲得表單中提交內容
List<FileItem> list = upLoad.parseRequest(request);
for (FileItem fileItem : list) {
// fileItem.isFormField()返回true表示是普通的表單組件
// fileItem.isFormField()返回false表示是上傳
// 代表普通的輸入框,獲取內容方式不同以往
if (fileItem.isFormField()) {
// getName()方法返回的是文件名字 普通表單組件有文件 返回NULL
String FieldName = fileItem.getFieldName();
String Content = fileItem.getString("UTF-8");
// 為了后面可以把普通參數從request中拿出來
request.setAttribute(FieldName, Content);
} else {
// 取得上傳文件的名字
String fileName = fileItem.getName();
// 避免文件名字重復
fileName = System.currentTimeMillis() + fileName;
File file = new File(realpath, fileName);
// 把上傳文件進行指定目錄
fileItem.write(file);
}
}
System.out.println("request.getAttribute(\"username\") = " + request.getAttribute("username"));
} catch (Exception e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
web.xml中該servlet的配置:
<servlet>
<description></description>
<display-name>Upload</display-name>
<servlet-name>Upload</servlet-name>
<servlet-class>com.briup.servlet.function.Upload</servlet-class>
<init-param>
<param-name>path</param-name>
<param-value>/upload</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Upload</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>
效果瀏覽: