JavaWeb筆記Day7------Servlet,Request和Response
web相關概念回顧
軟體架構
- C/S:客戶端/伺服器端
- B/S:瀏覽器/伺服器端
資源分類
- 靜態資源:所有使用者訪問後,得到的結果都是一樣的,稱為靜態資源,靜態資源可以直接被瀏覽器解析
- 如:HTML,css,JavaScript
- 動態資源:每個使用者訪問相同資源後,得到的結果可能不一樣。稱為動態資源。動態資源被訪問後,需要先轉換為靜態資源,再返回給瀏覽器
- 如servlet/jsp,php,asp...
網路通訊三要素
- IP:電子裝置(計算機)在網路中的唯一標識
- 埠:應用程式在計算機中的唯一標識 0~65536
- 傳輸協議:規定了資料傳輸的規則
- 基礎協議:
- tcp:安全協議,三次握手。速度稍慢
- udp:不安全協議。速度快
- 基礎協議:
web伺服器軟體
-
伺服器:安裝了伺服器軟體的計算機
-
伺服器軟體:接收使用者的請求,處理請求,做出響應
-
web伺服器軟體:接收使用者的請求,處理請求,做出響應
- 在web伺服器軟體中,可以部署web專案,讓使用者通過瀏覽器來訪問這些專案
- web容器
-
常見的Java相關的web伺服器軟體
- webLogic:oracle公司,大型的JavaEE伺服器,支援所有的JavaEE規範,收費的。
- webSphere:IBM公司,大型的JavaEE伺服器,支援所有的JavaEE規範,收費的。
- JBOSS:JBOSS公司的,大型的JavaEE伺服器,支援所有的JavaEE規範,收費的。
- Tomcat:Apache基金組織,中小型的JavaEE伺服器,僅僅支援少量的JavaEE規範servlet/jsp。開源的,免費的。
-
JavaEE:Java語言在企業級開發中使用的技術規範的總和,一共規定了13項大的規範
Tomcat
概念
web伺服器軟體
使用
-
安裝:解壓壓縮包即可。
- 注意:安裝目錄建議不要有中文和空格
-
解除安裝:刪除目錄就行了
-
啟動:
-
bin/startup.bat 雙擊執行即可
-
訪問:瀏覽器輸入:
-
可能遇到的問題
-
黑視窗一閃而過:
- 原因:沒有正確配置JAVA_HOME環境變數
- 解決方案:正確配置JAVA_HOME環境變數
-
啟動報錯:
-
暴力:找到佔用的埠號,並且找到對應的程序,殺死該程序
netstat -ano|findstr "埠號" taskkill /f /t /im PID號
-
溫柔:修改自身的埠號(conf/server.xml)
<Connector port="8888" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8445" />
-
一般會將tomcat的預設埠號修改為80。80埠號是http協議的預設埠號
- 好處:在訪問時就不用輸入埠號
-
-
-
-
關閉
- 正常關閉:
- bin/shutdown.bat
- ctrl+c
- 強制關閉:
- 點選啟動視窗的x
- 正常關閉:
-
配置
- 部署專案的方式:
-
直接將專案放到webapps目錄下即可
- /hello:專案的訪問路徑-->虛擬路徑
- 簡化部署:將專案打成一個war包,再將war包放置到webapps目錄下
- war包會自動解壓縮
-
配置conf/server.xml檔案
在<host>標籤體中配置
<Context docBase="D:\hello path="/hehe" />
- docBase:專案存放的路徑
- path:虛擬目錄
-
在conf\Catalina\localhost建立任意名稱的xml檔案。在檔案中編寫
<Context docBase="D:\hello />- 虛擬目錄:xml的檔名稱
-
- 部署專案的方式:
目錄結構
Java動態專案的目錄結構
---- 專案根目錄
---- WEB-INF目錄:
----web.xml:web專案的核心配置檔案
----classes目錄:放置位元組碼檔案的目錄
----lib目錄:放置依賴的jar包
Servlet:server applet
概念
執行在伺服器端的小程式
- Servlet就是一個介面,定義了Java類被瀏覽器訪問(Tomcat識別)到的規則。
- 將來我們自定義一個類,實現Servlet介面,複寫方法
步驟
-
建立JavaEE專案
-
定義一個類,實現Servlet介面
package web.servlet; import javax.servlet.*; import java.io.IOException; public class ServletDemo1 implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } //提供服務的方法 @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("Hello Servlet!"); } @Override public String getServletInfo() { return null; } @Override public void destroy() { } }
-
實現介面中的抽象方法
-
配置Servlet
-
在web.xml中配置
<!--配置Servlet--> <servlet> <servlet-name>helloServlet</servlet-name> <!--全類名--> <servlet-class>web.servlet.ServletDemo1</servlet-class> </servlet> <servlet-mapping> <servlet-name>helloServlet</servlet-name> <!--訪問路徑--> <url-pattern>/hello</url-pattern> </servlet-mapping>
-
執行原理
- 當伺服器接收到客戶端瀏覽器的請求後,會解析請求URL路徑,獲取訪問的Servlet的資源路徑。
- 查詢web.xml檔案,是否有對應的<url-pattern>標籤體內容。
- 如果有,則在找到對應的<servlet-class>全類名
- Tomcat會將位元組碼檔案載入進記憶體,並且建立其物件
- 呼叫其方法
Servlet中的生命週期
-
被建立:執行init方法(一般用於載入資源),只執行一次
-
Servlet什麼時候被建立
-
預設情況下,第一次被訪問時,Servlet被建立
-
可以配置執行Servlet的建立時機
-
在servlet標籤下配置
- 第一次被訪問時,建立
<load-on-startup>的值為負數 - 在伺服器啟動時,建立
<load-on-startup>的值為0或正整數
<load-on-startup>-1</load-on-startup>
- 第一次被訪問時,建立
-
-
-
Servlet的init方法,只執行一次,說明一個servlet在記憶體中只存在一個物件,Servlet是單例的
- 多個使用者同時訪問時,可能存線上程安全問題
- 解決:儘量不要在Servlet中定義成員變數。即使定義了成員變數,也不要對其修改值
-
-
提供服務:執行service方法,執行多次
- 每次訪問Servlet時,Servlet方法都會被呼叫一次
-
被銷燬:執行destroy方法,只執行一次
- Servlet被銷燬時執行。伺服器關閉時,Servlet被銷燬
- 只有伺服器正常關閉時,才會執行destroy方法
- destroy方法在Servlet被銷燬之前執行,一般用於釋放資源
package web.servlet;
import javax.servlet.*;
import java.io.IOException;
public class ServletDemo2 implements Servlet {
/**
* 初始化方法
* 在Servlet被建立時,執行。只會執行一次
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("ServletDemo2 init");
}
/**
* 獲取ServletConfig物件
* ServletConfig物件是Servlet的配置物件
* @return
*/
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 提供服務方法
* 每一次Servlet被訪問時,執行。執行多次
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("ServletDemo2 service");
}
/**
* 獲取Servlet的一些資訊,版本,作者等等...
* @return
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 銷燬方法
* 在Servlet被銷燬時(伺服器正常關閉時),執行。執行一次
*/
@Override
public void destroy() {
System.out.println("ServletDemo2 destroy");
}
}
Servlet3.0
好處
支援註解配置。可以不需要web.xml了
步驟
-
建立JavaEE專案,選擇Servlet的版本3.0以上,可以不建立web.xml
-
定義一個類,實現Servlet介面
-
複寫方法
-
在類上使用@WebServlet註解,進行配置
@WebServlet("資源路徑") @WebServlet(urlPatterns = "資源路徑")
IDEA與Tomcat的相關配置
Servlet的體系結構
-
Servlet------介面
|
GenericServlet------抽象類
|
HttpServlet-----抽象類 -
GenericServlet:將Servlet介面中其他的方法做了預設空實現
- 將來定義Servlet類可以繼承GenericServlet,實現service()方法即可
-
HttpServlet:對http協議的一種封裝,簡化操作
-
定義類繼承HTTPServlet
-
複寫doGet/doPost方法
-
原始碼如下:
/** * javax.servlet.http.HttpServlet protected void service(@NotNull HttpServletRequest req, * HttpServletResponse resp) throws ServletException, IOException * 從公共service方法接收標準 HTTP 請求,並將它們分派到此類中定義的do Method方法。此方法是javax.servlet.Servlet.service方法的 HTTP 特定版本。無需重寫此方法。 * * 參形: * req – HttpServletRequest物件,其中包含客戶端對 servlet 發出的請求 * resp – HttpServletResponse物件,其中包含 servlet 返回給客戶端的響應 * 丟擲: * IOException – 如果在 servlet 處理 HTTP 請求時發生輸入或輸出錯誤 * ServletException – 如果無法處理 HTTP 請求 * 請參閱: * javax.servlet.Servlet.service * 推斷註解: * @org.jetbrains.annotations.NotNull * apache-tomcat-9.0.8-src */ 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); } }
-
Servlet相關配置
- urlpartten:Servlet訪問路徑
- 一個Servlet可以定義多個訪問路徑:@WebServlet({"/d4","/dd4","/ddd4"})
- 路徑定義規則:
- /xxx
- /xxx/xxx:多層路徑,目錄結構
- *.do
HTTP協議
概念
Hyper Text Transfer Protocal 超文字傳輸協議
-
傳輸協議:定義了,客戶端和服務端通訊時,傳送資料的格式
-
特點:
- 基於TCP/IP的高階協議
- 預設埠號:80
- 基於請求響應模型的:一次請求對應一次響應
- 無狀態的:每次請求之間相互獨立,不能互動資料
-
歷史版本:
-
1.0:每一次請求響應都會建立新的連線
-
1.1:複用連線
-
請求訊息
概念
客戶端傳送給伺服器端的資料
資料格式
-
請求行
請求方式 請求url 請求協議/版本 GET /login.html HTTP/1.1 - 請求方式:
- http協議有7種請求方式,常用的有兩種
- GET:
- 請求引數在請求行中,在url後。
- 請求的url長度有限制的
- 不太安全
- POST:
- 請求引數在請求體中
- 請求的url長度沒有限制的
- 相對安全
- GET:
- http協議有7種請求方式,常用的有兩種
- 請求方式:
-
請求頭:客戶端瀏覽器告訴伺服器一些資訊
請求頭名稱:請求頭值
- 常見的請求頭:
-
User-Agent:瀏覽器告訴伺服器,我訪問你使用的瀏覽器版本資訊
- 可以在伺服器端獲取該頭的資訊,解決瀏覽器的相容性問題
-
Referer:http://localhost/login.html
- 告訴伺服器,我(當前請求)從哪裡來?
- 防盜鏈:
- 統計工作:
- 防盜鏈:
- 告訴伺服器,我(當前請求)從哪裡來?
-
- 常見的請求頭:
-
請求空行
空行,就是用於分割POST請求的請求頭,和請求體的。
-
請求體(正文)
- 封裝POST請求訊息的請求引數的
響應訊息
- 概念:伺服器端傳送給客戶端的資料
- 資料格式:
-
響應行
- 組成:協議/版本 響應狀態碼 狀態碼描述
- 響應狀態碼:伺服器告訴客戶端瀏覽器本次請求和響應的一個狀態
-
狀態碼都是三位數字
-
分類:
-
1xx:伺服器接收客戶端訊息,但沒有接收完成,等待一段時間後,傳送1xx狀態碼
-
2xx:成功。代表:200
-
3xx:重定向。代表:302(重定向),304(訪問快取)
-
4xx:客戶端錯誤。代表:404(請求路徑沒有對應的資源)405(請求方式沒有對應的doXxx方法)
-
5xx:伺服器端錯誤。代表:500(伺服器內部出現異常)
-
-
-
響應頭:
- 格式:頭名稱:值
- 常見的響應頭:
- Content-Type:伺服器告訴客戶端本次響應體資料格式以及編碼格式
- Content-disposition:伺服器告訴客戶端以什麼格式開啟響應體資料
- 值:
- in-line:預設值,在當前頁面內開啟
- attachment;filename=xxx:以附件形式開啟響應體。檔案下載
- 值:
-
響應空行
-
響應體:傳輸的資料
-
Request
request物件和response物件的原理
- request和response物件是由伺服器建立的。我們來使用它們
- request物件是來獲取請求訊息,response物件是來設定響應訊息
request物件繼承體系結構
request功能
獲取請求訊息
獲取請求行資料
- GET /day14/demo1?name=zhangsan HTTP/1.1
- 方法:
-
獲取請求方式:GET
String getMethod();
-
獲取虛擬目錄:/day14
String getContextPath();
-
獲取Servlet路徑:demo1
String getServletPath();
-
獲取get方式請求引數:name=zhangsan
String getQueryString();
-
獲取請求URI:/day14/demo1
//day14/demo1 String getRequestURI(); //http://localhost/day14/demo1 StringBuffer getRequestURL();
-
URL:統一資源定位符:http://localhost/day14/demo1
-
URI:統一資源識別符號:day14/demo1
-
-
獲取協議及版本:HTTP/1.1
String getProtocol();
-
獲取客戶機的IP地址:
String getRemoteAddr();
-
獲取請求頭資料
-
方法:
//通過請求頭的名稱獲取請求頭的值 String getHeader(String name); //獲取所有的請求頭名稱 Enumeration<String> getHeaderNames();
獲取請求體資料
-
請求體:只有POST請求方式,才有請求體,在請求體中封裝了POST請求的請求引數
-
步驟:
-
獲取流物件
//獲取字元輸入流,只能操作字元資料 BufferedReader getReader(); //獲取位元組輸入流,可以操作所有型別資料 ServletInputStream getInputStream();
-
再從流物件中拿資料
-
其他功能
獲取請求引數通用方式
不論get還是post請求方式都可以使用下列方法來獲取請求引數
//根據引數名稱獲取引數值 username=zs&password=123
String getParameter(String name);
//根據引數名稱獲取引數值的陣列
String[] getParameterValues(String name);
//獲取所有請求的引數名稱
Enumration<String> getParameterNames();
//獲取所有引數的Map集合
Map<String,String[]>getParameterMap();
-
中文亂碼問題:
-
get方式:Tomcat8已經將get方式亂碼問題解決了
-
post方式:會亂碼
-
解決:在獲取引數前,設定請求request的編碼
request.setCharacterEncoding("utf-8");
-
-
請求轉發
-
概念:一種在伺服器內部的資源跳轉方式
-
步驟:
-
通過request物件獲取請求轉發器物件:
RequestDispatcher getRequestDispatcher(String path);
-
使用RequestDispatcher物件來進行轉發
forward(ServletRequest request,Servlet Response response);
-
-
特點:
- 瀏覽器位址列路徑不發生變化
- 只能轉發到當前伺服器內部資源中
- 轉發是一次請求
共享資料
-
域物件:一個有作用範圍的物件,可以在範圍內共享資料
-
request域:代表一次請求的範圍,一般用於請求轉發的多個資源中共享資料
-
方法:
//儲存資料
void setAttribute(String name,Object obj);
//通過鍵獲取值
Object getAttitude(String name);
//通過鍵移除鍵值對
void removeAttribute(String name);
獲取ServletContext
ServletContext getServletContext();
案例:使用者登入
需求
-
編寫login.html登入頁面
username & password 兩個輸入框
-
使用Druid資料庫連線池技術,操作mysql,day14資料庫中user表
-
使用JdbcTemplate技術封裝JDBC
-
登入成功跳轉到SuccessServlet展示:登入成功!使用者名稱,歡迎您
-
登入失敗跳轉到FailServlet展示:登入失敗,使用者名稱或密碼錯誤
分析
步驟
-
建立專案,匯入html頁面,配置檔案,jar包
-
建立資料庫環境
CREATE DATABASE Login; USE Login; CREATE TABLE USER( id INTEGER PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) UNIQUE NOT NULL, PASSWORD VARCHAR(32) NOT NULL ); INSERT user VALUES('1','admin','123456');
-
建立包和User類
package web.test.domain; /** * 使用者的實體類 */ public class User { private int id; private String username; private String password; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
-
建立包和UserDao類
package web.test.dao; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import web.test.domain.User; import web.test.util.JDBCUtils; /** * 操作資料庫中User表的類 */ public class UserDao { //宣告JDBCTemplate物件共用 private JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource()); /** * 登入方法 * @param loginUser 只有使用者名稱和密碼 * @return user包含使用者全部資料 */ public User login(User loginUser){ try { //1.編寫sql String sql="SELECT *FROM user WHERE username=? AND PASSWORD=?"; //呼叫query方法 User user=template.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), loginUser.getUsername(),loginUser.getPassword()); return user; } catch (DataAccessException e) { e.printStackTrace();//記錄日誌 return null; } } }
-
編寫LoginServlet類
package web.test.web.servlet; import web.test.dao.UserDao; import web.test.domain.User; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet(name = "LoginServlet", value = "/LoginServlet") public class LoginServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.設定編碼 request.setCharacterEncoding("utf-8"); //2.獲取請求引數 String username = request.getParameter("username"); String password = request.getParameter("password"); //3.封裝user物件 User loginUser = new User(); loginUser.setUsername(username); loginUser.setPassword(password); //4.呼叫UserDao的login方法 UserDao dao=new UserDao(); User user=dao.login(loginUser); //5.判斷user if (user==null){ //登入失敗 request.getRequestDispatcher("/FailServlet").forward(request,response); }else { //登入成功 //儲存資料 request.setAttribute("user",user); //轉發 request.getRequestDispatcher("/SuccessServlet").forward(request,response); } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } }
-
編寫FailServlet和SuccessServlet類
-
login.html中form表單的寫法
- 虛擬目錄+Servlet的資源路徑
BeanUtils
概念
- 成員變數
- 屬性:setter和getter方法擷取後的產物
- 例如:getUsername()---->Username---->username
用途
工具類---簡化資料封裝,用於封裝JavaBean。
JavaBean------標準的Java類
- 要求:
- 類必須被public修飾
- 必須提供空參構造器
- 成員變數必須使用private
- 提供公共的setter和getter方法
- 功能:封裝資料
方法
setProperty();
getProperty();
//將map集合的鍵值對資訊,封裝到對應的JavaBean物件中
populate();
案例
//2. 獲取所有請求引數
Map<String, String[]>map=request.getParameterMap();
//3.建立User物件
User loginUser=new User();
//3.1使用BeanUtils封裝
try {
BeanUtils.populate(loginUser,map);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Respouse物件
功能
設定響應訊息
設定響應行
-
格式:HTTP/1.1 200 ok
-
設定狀態碼
setStatus(int sc);
設定響應頭
setHeader(String name,String value);
設定響應體
使用步驟:
-
獲取輸出流
//字元輸出流 PrintWriter getWriter(); //位元組輸出流 ServletOutputStream getOutputStream();
-
使用輸出流,將資料輸出到客戶端瀏覽器
案例
完成重定向
-
重定向:資源跳轉的方式
-
程式碼:
package web.response; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet(name = "responseDemo1", value = "/responseDemo1") public class responseDemo1 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("demo1...."); //訪問/responseDemo1,會自動跳轉到/responseDemo2資源 // //1. 設定狀態碼為302 // response.setStatus(302); // //2. 設定響應頭location // response.setHeader("location","/StudyJavaWeb01/responseDemo2"); //簡單的重定向方法 //動態獲取虛擬目錄 String contextPath=request.getContextPath(); response.sendRedirect(contextPath+"/responseDemo2"); } }
-
重定向的特點:redirect
- 位址列發生變化
- 重定向可以訪問其他站點(伺服器)的資源
- 重定向是兩次請求。不能使用request物件來共享資料
-
轉發的特點:forward
- 轉發位址列路徑不變
- 轉發只能訪問當前伺服器下的資源
- 轉發是一次請求。可以使用request物件來共享資料
-
路徑的寫法:
-
路徑分類
-
相對路徑:通過相對路徑不可以確定唯一資源
- 如:./index.html
- 不以/開頭,以.開頭路徑
- 規則:找到當前資源和目標資源之間的相對位置關係
- ./:當期資源
- ../:後退一級目錄
-
絕對路徑:通過絕對路徑可以確定唯一資源
-
如:http://localhost/response/demo1 /response/demo1
-
以/開頭的路徑
-
規則:判斷定義的路徑是給誰用的?判斷請求將來從哪兒發出
-
給客戶端瀏覽器使用:需要加虛擬目錄(專案的訪問路徑)
-
建議虛擬目錄動態獲取
String contextPath=request.getContextPath();
-
<a>,<form>重定向。。。
-
-
給伺服器使用:不需要加虛擬目錄
- 轉發路徑
-
-
-
-
伺服器輸出字元資料到瀏覽器
-
步驟:
-
獲取字元輸出流
-
輸出資料
//簡單的形式,設定編碼,在獲取流之前設定 response.setContentType("text/html;charset=utf-8");
-
-
注意:
亂碼問題:
- PrintWriter pw=response.getWriter();獲取的流的預設編碼是ISO-8859-1
- 設定該流的預設編碼
- 告訴瀏覽器響應體使用的編碼
-
程式碼:
package web.response; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; import java.io.PrintWriter; @WebServlet(name = "responseDemo3", value = "/responseDemo3") public class responseDemo3 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 獲取流物件之前,設定流的預設編碼 // response.setCharacterEncoding("utf-8"); //告訴瀏覽器,伺服器傳送的訊息體資料的編碼,建議瀏覽器使用該編碼解碼 // response.setHeader("content-type","text/html;charset=utf-8"); //簡單的形式,設定編碼,在獲取流之前設定 response.setContentType("text/html;charset=utf-8"); // 1. 獲取字元輸出流 PrintWriter pw=response.getWriter(); // 2. 輸出資料 pw.write("<h1>Hello response你好<h1>"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request,response); } }
伺服器輸出位元組資料到瀏覽器
- 步驟:
-
獲取字元輸出流
-
輸出資料
-
驗證碼
-
本質:圖片
-
目的:防止惡意表單註冊
-
程式碼
-
生成驗證碼
package web.response; import javax.imageio.ImageIO; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; @WebServlet(name = "CheckCodeServlet", value = "/CheckCodeServlet") public class CheckCodeServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int width = 100; int height =50; //1. 建立物件,在記憶體中畫圖(驗證碼圖片) BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //2.美化圖片 //2.1 填充背景色 Graphics g=image.getGraphics();//畫筆物件 g.setColor(Color.YELLOW);//填充顏色 g.fillRect(0,0,width,height);//填充矩形 //2.2畫邊框 g.setColor(Color.BLUE);//設定畫筆顏色 g.drawRect(0,0,width-1,height-1);//畫矩形 String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; //生成隨機角標 Random random = new Random(); for (int i = 1; i <= 4; i++) { int index = random.nextInt(str.length()); //獲取字元 char ch=str.charAt(index); //2.3 寫驗證碼 g.drawString(ch+"",width/5*i,height/2); } //2.4 畫干擾線 g.setColor(Color.RED); for (int i = 0; i < 10; i++) { //隨機生成座標點 int x1=random.nextInt(width); int x2=random.nextInt(width); int y1=random.nextInt(height); int y2=random.nextInt(height); g.drawLine(x1,y1,x2,y2); } //3.將圖片輸出到頁面展示 ImageIO.write(image, "jpg", response.getOutputStream()); } }
-
繫結事件
<!DOCTYPE html> <html lang="en"> <head> <title>Title</title> </head> <script> /** * 分析: * 點選超連結或者圖片,需要換一張 * 1.給超連結或者圖片繫結單擊事件 * * 2.重新設定圖片的src屬性值 */ window.onload = function(){ //1.獲取圖片物件 var img = document.getElementById("checkCode"); //2.繫結單擊事件 img.onclick=function(){ //加時間戳 var data = new Date().getTime(); img.src = "/StudyJavaWeb01/CheckCodeServlet?a="+data; } var change=document.getElementById("change"); change.onclick=function(){ var data = new Date().getTime(); img.src = "/StudyJavaWeb01/CheckCodeServlet?a="+data; } } </script> <body> <img src="/StudyJavaWeb01/CheckCodeServlet" alt="" id="checkCode"> <a id="change" href="javascript:void (0)">看不清?換一張</a> </body> </html>
-
ServletContext物件
概念
代表整個web應用,可以和程式的容器(伺服器)來通訊
獲取
- 通過request物件獲取
功能
-
獲取MIME型別:
-
MIME型別:在網際網路通訊過程中定義的一種檔案資料型別
- 格式:大型別/小型別 text/html image/jepg
-
獲取:
String getMimeType(String file);
-
-
域物件:共享資料
setAttribute(String name,Object value); getAttribute(String name); removeAttribute(String name);
ServletContext物件範圍:所有使用者請求的資料
-
獲取檔案的真實(伺服器)路徑
方法:
String getRealPath(String path)
案例----檔案下載
需求
- 頁面顯示超連結
- 點選超連結後彈出下載提示框
- 完成圖片檔案下載
分析
- 超連結指向的資源如果能夠被瀏覽器解析,則在瀏覽器中展示,如果不能解析,則彈出下載提示框。不滿足需求
- 任何資源都必須彈出下載提示框
- 任何響應頭設定資源的開啟方式:content-disposition:attachment;filename=xxx
步驟
- 定義頁面,編輯超連結href屬性,指向Servlet,傳遞資源名稱filename
- 定義Servlet
- 獲取檔名稱
- 使用位元組輸入流載入進記憶體
- 指定response的響應頭:content-disposition:attachment;filename=xxx
- 將資料寫出到response輸出流
中文檔名稱問題
- 解決思路
- 獲取客戶端使用的瀏覽器版本資訊
- 根據不同的版本資訊,設定filename的編碼方式不同
程式碼
主類
package web.servletcontext;
import web.utils.DownLoadUtils;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.FileInputStream;
import java.io.IOException;
@WebServlet(name = "DownloadServlet", value = "/DownloadServlet")
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.獲取請求引數,檔名稱
String fileName = request.getParameter("filename");
//2.使用位元組輸入流載入進記憶體
//2.1找到檔案伺服器路徑
ServletContext ServletContext = this.getServletContext();
String realPath = ServletContext.getRealPath("/img/" + fileName);
//2.2用位元組流關聯
FileInputStream fileInputStream = new FileInputStream(realPath);
//3.設定response響應頭
//3.1設定響應頭型別:content-type
String mimeType = ServletContext.getMimeType(realPath);
response.setHeader("content-type", mimeType);//獲取檔案的mime型別
//3.2設定響應頭開啟方式:content-disposition
//解決中文檔名問題
//1.獲取user-agent請求頭
String userAgent = request.getHeader("user-agent");
//2.使用工具類編碼檔名
fileName=DownLoadUtils.getFileName(userAgent, fileName);
response.setHeader("content-disposition", "attachment;filename=" + fileName);
//4.將輸入流的資料寫到輸出流中
ServletOutputStream outputStream = response.getOutputStream();
byte[] bytes = new byte[1024*8];
int len = 0;
while ((len = fileInputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, len);
}
fileInputStream.close();
}
}
下載頁面
<!DOCTYPE html>
<html lang="en">
<head>
<title>Title</title>
</head>
<body>
<a href="/StudyJavaWeb01/DownloadServlet?filename=圖片20220120085920.jpg">圖片</a>
</body>
</html>
工具類
package web.utils;
import java.util.Base64;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class DownLoadUtils {
public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("MSIE")) {
// IE瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐瀏覽器
Base64.Encoder encoder = Base64.getEncoder();
filename = "=?utf-8?B?" + encoder.encodeToString(filename.getBytes("utf-8")) + "?=";
} else {
// 其它瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}