javaweb的自學之路(狂神說)


1. 基本概念

1.1 前言

​ Java Web,是用Java技術來解決相關web互聯網領域的技術棧。web包括:web服務端和web客戶端兩部分。Java在客戶端的應用有java applet,不過使用得很少,Java在服務器端的應用非常的豐富,比如Servlet,JSP和第三方框架等等。Java技術對Web領域的發展注入了強大的動力。

1.2 web應用程序:

可以提供瀏覽器訪問的程序;

  1. a.html、b.html.….多個web資源,這些web資源可以被外界訪問,對外界提供服務;
  2. 你們能訪問到的任何一個頁面或者資源,都存在於這個世界的某一個角落的計算機上。
  3. URL
  4. 這個統一的web資源會被放在同一個文件夾下,web應用程序>Tomcat:服務器、
  5. 一個web應用由多部分組成(靜態web,動態web)
    • html,css
    • jsp,servlet
    • Java程序
    • jar包
    • 配置文件(Properties)

Web應用程序編寫完畢后,若想提供給外界訪問;需費一個服務器來統一管理。

1.3 靜態web

靜態網站特點:

  1. 靜態網站是最初的建站方式,瀏覽者所看到的每個頁面是建站者上傳到服務器上的一個 html ( htm )文件,這種網站每增加、刪除、修改一個頁面,都必須重新對服務器的文件進行一次下載上傳。網頁內容一經發布到網站服務器上,無論是否有用戶訪問,每個靜態網頁的內容都是保存在網站服務器上的,也就是說,靜態網頁是實實在在保存在服務器上的文件,每個網頁都是一個獨立的文件;
  2. 靜態網頁的內容相對穩定,因此容易被搜索引擎檢索;
  3. 靜態網頁沒有數據庫的支持,在網站制作和維護方面工作量較大,因此當網站信息量很大時完全依靠靜態網頁制作方式比較困難;
  4. 靜態網頁的交互性較差,在功能方面有較大的限制。如:不能實現用戶注冊和用戶登錄的功能

靜態web原理圖如下圖所示:

img

1.4 動態web

所謂 “ 動態 ” ,並不是指網頁上簡單的 GIF 動態圖片或是 Flash 動畫,動態網站的概念現在還沒有統一標准,但都具備以下幾個基本特征:

  1. 交互性:網頁會根據用戶的要求和選擇而動態地改變和響應,瀏覽器作為客戶端,成為一個動態交流的橋梁,動態網頁的交互性也是今后 Web 發展的潮流。
  2. 自動更新:即無須手動更新 HTML 文檔,便會自動生成新頁面,可以大大節省工作量。
  3. 因時因人而變:即當不同時間、不同用戶訪問同一網址時會出現不同頁面。

動態web原理圖如下圖所示:

img

NOTE:

​ 動態網站在頁面里嵌套了程序,這種網站對一些框架相同,更新較快的信息頁面進行內容與形式的分離,將信息內容以記錄的形式存入了網站的數據庫中,以便於網 站各處的調用。這樣,我們看到的一個頁面,可能在服務器上不一一對應某個 html 的文件了,網頁框架里套了很多數據庫里的記錄中的內容。此外動態網頁是與靜態網頁相對應的,也就是說,網頁 URL 的后綴不是 .htm 、 .html 、 .shtml 、 .xml 等靜態網頁的常見形式,而是以 .asp 、 .jsp 、 .php 、 .perl 、 .cgi 等形式為后綴。

​ 從網站瀏覽者的角度來看,無論是動態網頁還是靜態網頁,都可以展示基本的文字和圖片信息,但從網站開發、管理、維護的角度來看就有很大的差別。網絡營銷教學網站將動態網頁的一般特點簡要歸納如下 :
   (1) 動態網頁以數據庫技術為基礎,可以大大降低網站維護的工作量 ;
   (2) 采用動態網頁技術的網站可以實現更多的功能,如用戶注冊、用戶登錄、在線調查、用戶管理、訂單管理等等 ;
   (3) 動態網頁實際上並不是獨立存在於服務器上的網頁文件,只有當用戶請求時服務器才返回一個完整的網頁 ;

2、web服務器

2.1 技術講解

ASP:

動態服務器網頁(英文:Active Server Pages,簡稱ASP),由微軟公司開發的服務器端運行的腳本平台,它被Windows下Internet Information Services (IIS)的程序所管理。特點:

  • 微軟:國內最早流行的就是ASP;
  • 在HTML中嵌入了VB的腳本, ASP + COM;
  • 在ASP開發中,基本一個頁面都有幾千行的業務代碼,頁面極其換亂
  • 維護成本高!
  • C#

php:

  • PHP開發速度很快,功能很強大,跨平台,代碼很簡單 (70% , WP)
  • 無法承載大訪問量的情況(局限性)

**JSP/Servlet : **

  • sun公司主推的B/S架構
  • 基於Java語言的 (所有的大公司,或者一些開源的組件,都是用Java寫的)
  • 可以承載三高問題帶來的影響;
  • 語法像ASP , ASP–>JSP , 加強市場強度;

1、不同之處在哪?

  • Servlet在Java代碼中通過HttpServletResponse對象動態輸出HTML內容
  • JSP在靜態HTML內容中嵌入Java代碼,Java代碼被動態執行后生成HTML內容

2、各自的特點

  • Servlet能夠很好地組織業務邏輯代碼,但是在Java源文件中通過字符串拼接的方式生成動態HTML內容會導致代碼維護困難、可讀性差
  • JSP雖然規避了Servlet在生成HTML內容方面的劣勢,但是在HTML中混入大量、復雜的業務邏輯同樣也是不可取的

可以通過mvc模式使兩者結合起來:

img

2.2 web服務器

服務器是一種被動的操作,用來處理用戶的一些請求和給用戶一些響應信息;

IIS

微軟的; ASP…,Windows中自帶的

Tomcat

Tomcat是Apache 軟件基金會(Apache Software Foundation)的Jakarta 項目中的一個核心項目,最新的Servlet 和JSP 規范總是能在Tomcat 中得到體現,因為Tomcat 技術先進、性能穩定,而且免費,因而深受Java 愛好者的喜愛並得到了部分軟件開發商的認可,成為目前比較流行的Web 應用服務器。

Tomcat 服務器是一個免費的開放源代碼的Web 應用服務器,屬於輕量級應用服務器,在中小型系統和並發訪問用戶不是很多的場合下被普遍使用,是開發和調試JSP 程序的首選。對於一個Java初學web的人來說,它是最佳的選擇

Tomcat 實際上運行JSP 頁面和Servlet。Tomcat最新版本為9.0。

3. Tomcat

3.1 tomcat文件夾信息

img

3.2 核心配置文件

image-20200727075350699

默認端口號:8080

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"/>

默認主機名和默認存放網站應用的文件夾

<Host name="localhost"  appBase="webapps"
      unpackWARs="true" autoDeploy="true">

3.3 發布一個網站

將自己寫的網站,放到服務器(Tomcat)中指定的web應用的文件夾(webapps)下,就可以訪問了

網站應有的結構:

--webapps :Tomcat服務器的web目錄
	-ROOT
	-kuangstudy :網站的目錄名
		- WEB-INF
			-classes : java程序
			-lib:web應用所依賴的jar包
			-web.xml :網站配置文件
		- index.html 默認的首頁
		- static 
            -css
            	-style.css
            -js
            -img
         -.....

4. Http協議

4.1 HTTP簡介

HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於從萬維網(WWW:World Wide Web )服務器傳輸超文本到本地瀏覽器的傳送協議。

HTTP協議工作於客戶端-服務端架構為上。瀏覽器作為HTTP客戶端通過URL向HTTP服務端即WEB服務器發送所有請求。Web服務器根據接收到的請求后,向客戶端發送響應信息。

img

4.2 主要特點

1、簡單快速:客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST。每種方法規定了客戶與服務器聯系的類型不同。由於HTTP協議簡單,使得HTTP服務器的程序規模小,因而通信速度很快。

2、靈活:HTTP允許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type加以標記。

3.無連接:無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答后,即斷開連接。采用這種方式可以節省傳輸時間。

4.無狀態:所謂http是無狀態協議,言外之意是說http協議沒法保存客戶機信息,也就沒法區分每次請求的不同之處。關於http無狀態阻礙了交互式應用程序的實現。比如記錄用戶瀏覽哪些網頁、判斷用戶是否擁有權限訪問等。於是,兩種用於保持HTTP狀態的技術就應運而生了,一個是Cookie,而另一個則是Session。
5、支持B/S及C/S模式。

4.3 HTTP之URL

HTTP使用統一資源標識符(Uniform Resource Identifiers, URI)來傳輸數據和建立連接。URL是一種特殊類型的URI(URL則通過描述是哪個主機上哪個路徑上的文件來唯一確定一個資源,也就是定位的方式來實現的URI),URL,全稱是UniformResourceLocator, 中文叫統一資源定位符,是互聯網上用來標識某一處資源的地址。以下面這個URL為例,介紹下普通URL的各部分組成:

http://www.aspxfans.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name

從上面的URL可以看出,一個完整的URL包括以下幾部分:
1.協議部分:該URL的協議部分為“http:”,這代表網頁使用的是HTTP協議。在Internet中可以使用多種協議,如HTTP,FTP等等本例中使用的是HTTP協議。在"HTTP"后面的“//”為分隔符

2.域名部分:該URL的域名部分為“www.aspxfans.com”。一個URL中,也可以使用IP地址作為域名使用

3.端口部分:跟在域名后面的是端口,域名和端口之間使用“:”作為分隔符。端口不是一個URL必須的部分,如果省略端口部分,將采用默認端口

4.虛擬目錄部分:從域名后的第一個“/”開始到最后一個“/”為止,是虛擬目錄部分。虛擬目錄也不是一個URL必須的部分。本例中的虛擬目錄是“/news/”

5.文件名部分:從域名后的最后一個“/”開始到“?”為止,是文件名部分,如果沒有“?”,則是從域名后的最后一個“/”開始到“#”為止,是文件部分,如果沒有“?”和“#”,那么從域名后的最后一個“/”開始到結束,都是文件名部分。本例中的文件名是“index.asp”。文件名部分也不是一個URL必須的部分,如果省略該部分,則使用默認的文件名

6.錨部分:從“#”開始到最后,都是錨部分。本例中的錨部分是“name”。錨部分也不是一個URL必須的部分

7.參數部分:從“?”開始到“#”為止之間的部分為參數部分,又稱搜索部分、查詢部分。本例中的參數部分為“boardID=5&ID=24618&page=1”。參數可以允許有多個參數,參數與參數之間用“&”作為分隔符。

4.4 Http請求

客戶端發送請求(Request)給服務器

百度

Request URL:https://www.baidu.com/   請求地址
Request Method:GET    get方法/post方法
Status Code:200 OK    狀態碼:200
Remote(遠程) Address:14.215.177.39:443
Accept:text/html  
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.9    語言
Cache-Control:max-age=0
Connection:keep-alive
1、請求行
  • 請求行中的請求方式:GET
  • 請求方式:Get,Post,HEAD,DELETE,PUT,TRACT…
    • get:請求能夠攜帶的參數比較少,大小有限制,會在瀏覽器的URL地址欄顯示數據內容,不安全,但高效
    • post:請求能夠攜帶的參數沒有限制,大小沒有限制,不會在瀏覽器的URL地址欄顯示數據內容,安全,但不高效。
2、消息頭
Accept:告訴瀏覽器,它所支持的數據類型
Accept-Encoding:支持哪種編碼格式  GBK   UTF-8   GB2312  ISO8859-1
Accept-Language:告訴瀏覽器,它的語言環境
Cache-Control:緩存控制
Connection:告訴瀏覽器,請求完成是斷開還是保持連接
HOST:主機..../.

4.4、Http響應

  • 服務器發出響應給客戶端

百度

Cache-Control:private    緩存控制
Connection:Keep-Alive    連接
Content-Encoding:gzip    編碼
Content-Type:text/html   類型
1.響應體
Accept:告訴瀏覽器,它所支持的數據類型
Accept-Encoding:支持哪種編碼格式  GBK   UTF-8   GB2312  ISO8859-1
Accept-Language:告訴瀏覽器,它的語言環境
Cache-Control:緩存控制
Connection:告訴瀏覽器,請求完成是斷開還是保持連接
HOST:主機..../.
Refresh:告訴客戶端,多久刷新一次;
Location:讓網頁重新定位;
2、響應狀態碼

200:請求響應成功 200

3xx:請求重定向

  • 重定向:你重新到我給你新位置去;

4xx:找不到資源 404

  • 資源不存在;

5xx:服務器代碼錯誤 500 502:網關錯誤

其他部分請參考鏈接: http協議一篇就夠了

5. Maven

5.1 簡介

Maven是一個采用純Java編寫的開源項目管理工具。Maven采用了一種被稱之為project object model (POM)概念來管理項目,所有的項目配置信息都被定義在一個叫做POM.xml的文件中,通過該文件,Maven可以管理項目的整個聲明周期,包括編譯,構建,測試,發布,報告等等。目前Apache下絕大多數項目都已經采用Maven進行管理。而Maven本身還支持多種插件,可以方便更靈活的控制項目。一句話:Maven是一個項目管理和構建工具,主要做編譯、測試、報告、打包、部署等操作完成項目的構建。Maven不僅是構建工具,還是一個依賴管理工具和項目管理工具,它提供了中央倉庫,能幫我自動下載構件。

5.2 配置

配置鏡像(鏡像(Mirroring)是一種文件存儲形式,是冗余的一種類型,一個磁盤上的數據在另一個磁盤上存在一個完全相同的副本即為鏡像。),使用默認倉庫下載jar包會比較慢,所以配置一個阿里雲鏡像

打開maven安裝目錄下的conf\settings.xml 添加如下信息

	<mirror>
      <id>nexus-aliyun</id>
	  <mirrorOf>central</mirrorOf>
	  <name>Nexus aliyun</name>
	  <url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
	</mirror>

添加本地倉庫路徑:

<localRepository>E:\Environment\apache-maven-3.6.3\maven-repo</localRepository>

5.3 pom.xml的結構

<?xml version="1.0" encoding="UTF-8"?>
<!--頭文件和maven版本 xmlns:xml name space-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <!--剛剛配置的GAV-->
  <groupId>com.iandf</groupId>
  <artifactId>javaweb-01-maven</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>javaweb-01-maven Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <!--項目默認的構建編碼-->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!--編碼版本-->
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>
  <!--項目依賴-->
  <dependencies>
    <!--具體的jar包配置文件-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <!--項目構建需要用到的東西-->
  <build>
    <finalName>javaweb-01-maven</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
 		。。。。
      </plugins>
    </pluginManagement>
  </build>
</project>

5.5 web.xml中webapp版本問題

自動生成的webapp是2.6版本的,最新版的版本在tomcat中可以找到

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0"
         metadata-complete="true">
</web-app>

5.5 maven其他

跟我1小時搞定Maven是什么?怎么用?

6 servlet

6.1 簡介

  1. servlet是sun公司用來開發動態web的工具
  2. sun在API中提供了一個接口叫做 servlet,開發一個servlet程序需要兩個步驟
    • 編寫一個java類實現servlet接口
    • 把編寫好的java類部署到web服務器上

6.2 編寫helloServlet程序

servlet接口sun公司提供了兩個默認的實現類: httpServlet,genericServlet

  • 構建一個普通的Maven項目,刪掉里面的src目錄,在這個項目里面建立Moudel(模塊),這個空的工程就是Maven主工程;
  • 在Maven父子工程中
  • 父項目的pom.xml中自動生成
<!--讀pom文件時,要去讀modules下的子pom文件-->
<modules>
    <module>HelloServlet</module>
</modules>
  • 子類項目的pom.xml自動生成

    <!--使子項目繼承父項目的設置,避免導入重復依賴 eg:son extends parent-->
    <parent>
        <artifactId>javaweb-02-servlet</artifactId>
        <groupId>com.iandf</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    
  • 編寫一個Servlet程序

    在這里插入圖片描述

  • 編寫一個普通類,實現Servlet接口,這里我們直接繼承HttpServlet(HttpServlet實現了Servlet接口

    public class HelloServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            PrintWriter writer = resp.getWriter();//獲取響應流
            //響應流輸出信息
            writer.print("hello servlet");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  • 編寫servlet映射

    我們編寫的java程序需要被瀏覽器訪問,而瀏覽器需要訪問web服務器,所以我們需要在web服務器中部署servlet,給他一個訪問的具體路徑。所以我們在web.xml中注冊servlet和servlet-mapping

      <!--注冊servlet-->
      <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.iandf.HelloServlet</servlet-class>
      </servlet>
      <!--每一個servlet對應一個映射-->
      <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
      </servlet-mapping>
    
  • 配置tomcat

  • 啟動項目

    啟動后通過請求路徑訪問程序 --- 域名/發布路徑/請求路徑 如: http://localhost:8080/HelloServlet_war/hello

6.3 servlet原理

tomcat 與 servlet配合工作 步驟:

  1. web client 向servlet容器(tomcat)發出請求
  2. tomcat接收web client的請求
  3. servlet容器創建一個httpResquest對象,將請求的信息封裝到對象中
  4. servlet容器創建一個httpResponse對象,
  5. servlet容器調用HttpServlet對象的service(),把httpResquest和httpResponse傳給HttpServlet對象
  6. httpServlet調用httpRequest對象的有關方法,獲取http請求的信息,如 RequestURL:https://www.baidu.com Request Method: GET
  7. HttpServlet調用HttpResponse對象的有關方法,生成響應數據。
  8. Servlet容器把HttpServlet的響應結果傳給Web Client。

如下圖所示:

圖片描述

參考博客:

https://www.cnblogs.com/java-chen-hao/p/10688617.html#_label2_0

https://segmentfault.com/a/1190000013101861

6.4 ServletContext

當web容器啟動時,會創建一個servletContext對象,可以通過這個對象讓servlet之間進行通信,代表當前的web應用

1. 共享數據

我們在一個Servlet中通過servlerContext對象保存(set)的數據,可以在另一個servlet通過servlerContext對象get到

public class SetServletContext extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        String name =  "黃鶴";

        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        servletContext.setAttribute("username",name);

    }
}
public class GetServletContext extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        String username = (String) servletContext.getAttribute("username");

        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().print("username : " + username);
    }
}

先訪問SetServletContext設置數據,再訪問GetServletContext就可以拿到數據了。

2. 獲取配置參數
String db_url = this.getServletContext().getInitParameter("db_url");

resp.getWriter().print(db_url);
3. 請求轉發

note: 請求轉發和重定向的區別

forward(轉發)

是服務器請求資源,服務器直接訪問目標地址的URL,把那個URL的響應內容讀取過來,然后把這些內容再發給瀏覽器.瀏覽器根本不知道服務器發送的內容從哪里來的,因為這個跳轉過程實在服務器實現的,並不是在客戶端實現的所以客戶端並不知道這個跳轉動作,所以它的地址欄還是原來的地址.

redirect(重定向)

是服務端根據邏輯,發送一個狀態碼,告訴瀏覽器重新去請求那個地址.所以地址欄顯示的是新的URL.

轉發是服務器行為,重定向是客戶端行為。

RequestDispatcher requestDispatcher = this.getServletContext().getRequestDispatcher("/getParams");//獲取請求轉發的路徑
requestDispatcher.forward(req,resp);//使用forward方法將參數傳給請求轉發的路徑,實現請求轉發
4. 讀取配置文件
//文件路徑為tomcat生成的webapp文件夾下的資源路徑/WEB-INF/classes/db.properties,第一個/代表當前項目
InputStream resourceAsStream = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);

String username = properties.getProperty("username");
String password = properties.getProperty("password");

resp.getWriter().print(username+"  :"+password);

6.5 HttpServletResponse

web服務器接收了web client的請求,生成兩個對象:HttpServletResponse和HttpServletRequest

  1. response負責把數據傳給瀏覽器
  2. request負責讀取client請求的信息

使用HttpServletResponse讓瀏覽器下載文件

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //獲取文件路徑
    String filePath = "D:\\ForStrong_java\\javaweb\\javaweb-02-servlet\\ServletHttpResponse\\target\\classes\\1.jpg";
    //獲取文件名
    String fileName = filePath.substring(filePath.lastIndexOf("\\") + 1);
    //獲取文件輸入流
    BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
    //獲取向瀏覽器的輸出流
    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(resp.getOutputStream());
    //讓瀏覽器知道我們要下載文件,需要設置頭,讓瀏覽器支持。中文文件名URLEncoder.encode編碼,否則有可能亂碼
    resp.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName,"utf-8"));
    //向瀏覽器輸出數據
    byte[] buffer = new byte[1024];
    int len = 0;
    while ((len = bufferedInputStream.read(buffer))>0){
        bufferedOutputStream.write(buffer,0,len);
    }
    //關閉流
    bufferedInputStream.close();
    bufferedOutputStream.close();
}

response實現驗證碼,三秒刷新一次

 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //寫一張圖片
        BufferedImage bufferedImage = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);//需要一張畫板
        Graphics graphics = bufferedImage.getGraphics();//需要一只畫筆
        //設置背景
        graphics.setColor(Color.white);//設置顏色
        graphics.fillRect(0,0,80,20);
        //設置內容
        graphics.setColor(Color.blue);
        graphics.setFont(new Font(null,Font.ITALIC,20));
        graphics.drawString(Objects.requireNonNull(makeNum()),0,20);

        //告訴瀏覽器,這個請求用圖片的方式打開
        resp.setContentType("image/jpeg");
        //網站存在緩存,不讓瀏覽器緩存
        resp.setDateHeader("expires",-1);
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Pragma","no-cache");
        //給這個響應設置三秒刷新一次,且把緩存機制關掉
        resp.setHeader("refresh","3");
        //將這張圖片寫入瀏覽器
        ImageIO.write(bufferedImage,"jpg",resp.getOutputStream());
}

response實現重定向,eg:應用在用戶登錄頁面

 //參數為:項目名/地址名
resp.sendRedirect("/ServletHttpResponse/getImage");
<%--action:這里提交的路徑,需要尋找到項目的路徑--%>
<%--${pageContext.request.contextPath}代表當前的項目--%>
<form action="${pageContext.request.contextPath}/redirect" method="get">
    username <input name="username" type="text"> <br>
    password: <input name="password" type="password"> <br>
    <input type="submit">
</form>
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println(username +":  "+password);
        //參數為:項目名/地址名
        resp.sendRedirect("/ServletHttpResponse/success.jsp");
    }

6.6 HttpServletRequest

request獲取請求的數據,並請求轉發

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");

        //獲取表單數據
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobbies = req.getParameterValues("hobbies");
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(hobbies));

        //請求轉發
        req.getRequestDispatcher("/success.jsp").forward(req,resp);
    }
<form action="${pageContext.request.contextPath}/login" method="post">
    username <input name="username" type="text"> <br>
    password: <input name="password" type="password"> <br>
    hobbies:
    <input name="hobbies" type="checkbox" value="女孩">女孩
    <input name="hobbies" type="checkbox" value="代碼">代碼
    <input name="hobbies" type="checkbox" value="電影">電影
    <input name="hobbies" type="checkbox" value="爬山">爬山
    <input type="submit">
</form>

7 Cookie、Session

7.1、會話

會話:用戶打開一個瀏覽器,點擊了很多超鏈接,訪問多個web資源,關閉瀏覽器,這個過程可以稱之為會話;

有狀態會話:一個同學來過教室,下次再來教室,我們會知道這個同學,曾經來過,稱之為有狀態會話;

一個網站,怎么證明你來過?

客戶端 服務端

  1. 服務端給客戶端一個 信件,客戶端下次訪問服務端帶上信件就可以了; cookie
  2. 服務器登記你來過了,下次你來的時候我來匹配你; seesion

7.2、保存會話的兩種技術

cookie

  • 客戶端技術 (響應,請求)

session

  • 服務器技術,利用這個技術,可以保存用戶的會話信息? 我們可以把信息或者數據放在Session中!

常見常見:網站登錄之后,你下次不用再登錄了,第二次訪問直接就上去了!

  1. 由服務器創建一個cookie,並設置數據和屬性,再由服務器發送給客戶端,客戶端在本地保存該cookie

    //服務器給客戶端設置cookie
    Cookie time_cookie = new Cookie("lastTime",System.currentTimeMillis()+"");
    time_cookie.setMaxAge(24*60*60);//設置cookie的存活時間,0代表刪除cookie,負數代表不會保存該cookie,正數以秒為單位
    resp.addCookie(time_cookie);
    
  2. 下次訪問時帶上該cookie,服務器就能知道你先前來過,並可以通過cookie獲取數據

    //服務器獲取客戶端的cookie
    Cookie[] cookies = req.getCookies();
    PrintWriter writer = resp.getWriter();
    for (Cookie cookie : cookies) {
    if (cookie.getName().equals("lastTime")) {
    writer.print("上次登陸時間為:  ");
    writer.print(new SimpleDateFormat().format(new Date(Long.valueOf(cookie.getValue()))));
    

7.4 seesion

  1. 由request對象獲取session,如果沒有則創建session,在session中保存數據,服務器把sessionID以cookie的形式發送給客戶端

    /*     Returns the current session associated with this request,
         or if the request does not have a session, creates one.*/
    HttpSession session = req.getSession();
    String sessionId = session.getId();
    
    if (session.isNew()){
        resp.getWriter().print("session創建完成,sessionID為 :"+sessionId);
    }else {
        resp.getWriter().print("session已經在服務器中存在,sessionID為 :"+sessionId);
    }
      //給session設置數據
    session.setAttribute("person",new Person("huangaa",202160211));
    
  2. 服務器把sessionID以cookie的形式發送給客戶端,相當於這段代碼

     //每個用戶對應一個session,服務器會把sessionID保存在JSESSIONID的Cookie中發送給客戶端,存活時間是這次會話(expiry = -1),瀏覽器退出則刪除
     Cookie cookie = new Cookie("JSESSIONID",sessionId);
     cookie.setMaxAge(-1);
     resp.addCookie(cookie);
    
  3. session的存活時間可以在web.xml中進行配置

      <session-config>
        <!--以分鍾為單位-->
        <session-timeout>1</session-timeout>
      </session-config>
    
  4. 下次請求時,客戶端會把該cookie帶上,服務器可以通過該cookie獲取session,再通過session獲取數據,如果將該cookie在客戶端移除,服務器端將拿不到這個session。所以服務端的session的實現對客戶端的cookie有依賴關系的

    HttpSession session = req.getSession();
    Person person = (Person) session.getAttribute("person");
    resp.getWriter().print(person.toString());
    
  5. 刪除session

    HttpSession session = req.getSession();
    session.invalidate();
    

7.5 cookie和seesion的相同點和不同點

**相同點: **

cookie和session都是為了解決http協議無狀態的特征。

區別:

  1. cookie數據是存放在客戶端本地的,session數據是存放在服務器的,但是服務端的session的實現對客戶端的cookie有依賴關系的;
  2. cookie不是很安全,別人可以分析存放在本地的COOKIE並進行COOKIE欺騙,考慮到安全應當使用session;
  3. session會在一段時間內存放在服務器,如果session過多,會導致服務器壓力過大,性能降低。如果考慮服務器性能方面應該使用cookie
  4. cookie的大小是有限制的
  5. 一個用戶在一個站點上可以有多個cookie,但是只有一個session

8 JSP

簡介:JSP(JavaServer Pages)是由Sun Microsystems公司主導創建的一種動態網頁技術標准。JSP部署於網絡服務器上,可以響應客戶端發送的請求,並根據請求內容動態地生成HTMLXML或其他格式文檔的Web網頁,然后返回給請求者。JSP技術以Java語言作為腳本語言,為用戶的HTTP請求提供服務,並能與服務器上的其它Java程序共同處理復雜的業務需求。

8.1 JSP原理探究

在C:\Users\IANDF\AppData\Local\JetBrains\IntelliJIdea2020.1\tomcat\Unnamed_javaweb-04-jsp\work\Catalina\localhost\jsp_01\org\apache\jsp有

image-20200802094038119

說明xxx.jsp文件最終都會被服務器轉換成xxx.java,xxx.class文件。

index_jsp類結構

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
    
public abstract class HttpJspBase extends HttpServlet

index_jsp繼承了HttpJspBase,查看HttpJspBase源碼可以得到HttpJspBase繼承自HttpServlet

這說明jsp最終會裝換成servlet

部分方法

//初始化
public void _jspInit()
//銷毀
public void _jspDestroy() 
//服務
public void _jspService(HttpServletRequest request, HttpServletResponse response)

和servlet一樣,有以上的生命周期

_jspService()中的部分對象,這些對象可以在jsp文件中直接使用

final javax.servlet.jsp.PageContext pageContext;//頁面上下文
javax.servlet.http.HttpSession session = null;//session
final javax.servlet.ServletContext application;//ServletContext,代表整個web網站
final javax.servlet.ServletConfig config;//ServletConfig 配置對象
javax.servlet.jsp.JspWriter out = null;//out等同於resp.getWriter()
final java.lang.Object page = this;//當前頁面

_jspService()中的執行語句

response.setContentType("text/html");
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("<html>\n");
out.write("<body>\n");
out.write("<h2>Hello World!</h2>\n");
out.write("</body>\n");
out.write("</html>\n");

所有的html代碼都以out.write(xxx)的形式輸出

用戶拿jsp頁面的流程如下圖所示:

image-20200802102412257

8.2 JSP語法

1. jsp表達式
  <%--<%= 變量或者表達式%>--%>
  <%=new SimpleDateFormat("hh:mm:ss").format(new Date(System.currentTimeMillis())) %>
2. jsp聲明(全局變量和全局函數),默認生成在_jspService()中
  <%!
  static {
    System.out.println("該類已被訪問");
  }
  private int globalVar = 0;
  public int getGlobalVar(){
      return globalVar;
  }
  %>
3.jsp腳本片段
<%for (int i = 0; i < 5; i++) {
out.println(getGlobalVar());
%>
<h1>hello world <%= i%></h1>
<% } %>
8.3 JSP指令
<%@page args.... %>
<%@include file=""%>

<%--@include會將兩個頁面合二為一--%>
<%@include file="common/header.jsp"%>
<h1>網頁主體</h1>

<%@include file="common/footer.jsp"%>

<hr>


<%--jSP標簽
    jsp:include:拼接頁面,本質還是三個java文件
    --%>
<jsp:include page="/common/header.jsp"/>
<h1>網頁主體</h1>
<jsp:include page="/common/footer.jsp"/>

8.3 9大內置對象和其作用域

PageContext 存東西
Request 存東西
Response
Session 存東西
Application 【SerlvetContext】 存東西
config 【SerlvetConfig】
out
page ,不用了解
exception

四大作用域

存數據

public static final int PAGE_SCOPE = 1;
public static final int REQUEST_SCOPE = 2;
public static final int SESSION_SCOPE = 3;
public static final int APPLICATION_SCOPE = 4;
<%--存數據--%>
<%
    pageContext.setAttribute("pageContextName","pageContext");//保存的數據在當前頁面有效
    request.setAttribute("requestName","request");//保存的數據在一次請求中有效,請求轉發也會攜帶這個數據
    session.setAttribute("sessionName","session");//保存的數據在一次會話中有效,從打開瀏覽器到關閉瀏覽器
    application.setAttribute("applicationName","application");//保存的數據在服務器中有效
4
    
    \
    
    \]
    
    
    
    ,從打開服務器到關閉服務器
%>

源碼中存數據做的事情

public void setAttribute (String name,Object attribute,int scope){
    switch (scope) {
    case PAGE_SCOPE:
      mPage.put (name, attribute);
      break;
    case REQUEST_SCOPE:
      mRequest.put (name, attribute);
      break;
    case SESSION_SCOPE:
      mSession.put (name, attribute);
      break;
    case APPLICATION_SCOPE:
      mApp.put (name, attribute);
      break;
    default:
      throw new IllegalArgumentException  ("Bad scope " + scope);
    }
}

取數據,從作用域最小的開始找,如果存的時候有Key是同名的,數據將會取不出來

<%
    String pageContextName = (String) pageContext.findAttribute("pageContextName");
    String requestName = (String) request.getAttribute("requestName");
    String sessionName = (String) session.getAttribute("sessionName");
    String applicationName = (String) application.getAttribute("applicationName");
%>

源碼如下

//從作用域最小的開始找
public Object findAttribute (String name){
    if (mPage.containsKey (name)) {
      return mPage.get (name);
    }
    else if (mRequest.containsKey (name)) {
      return mRequest.get (name);
    }
    else if (mSession.containsKey (name)) {
      return mSession.get (name);
    }
    else if (mApp.containsKey (name)) {
      return mApp.get (name);
    }
    else {
      return null;
    }
}

8.4 JSTL標簽、EL表達式

第一步就是添加依賴

<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

EL表達式:${}

  • 獲取數據
  • 執行運算
  • 獲取web開發的常用對象

image-20200803111642332

測試代碼

<%--
跳轉到coreif.isp頁面參數為:username=param.username
完整路徑為http://localhost:8080/jsp/coreif.jsp?username=admin
--%>
<form action="coreif.jsp" method="get">
    <%--
    EL表達式獲取表單中的數據
    ${param.參數名}
    --%>
    <input type="text" name="username" value="${param.username}">
    <input type="submit" value="登錄">
</form>

<%--判斷如果提交的用戶名是管理員,則登錄成功--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
    <c:out value="管理員歡迎您!"/>
</c:if>

<%--自閉合標簽--%>
<c:out value="${isAdmin}"/>

9 mvc

mvc三層架構如下

image-20200803205327056

以登錄頁面到首頁為例子,流程如下

image-20200803205248251

10 Filter

10.1 filter工作原理圖

image-20200803214403951

10.2 filter的用法

  1. 讓java類實現javax.servlet.Filter接口

        //服務器啟動時就執行對filter進行初始化,隨時等待過濾特有的鏈接
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("CharacterEnCodingFilter已經啟動");
        }
    
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            servletRequest.setCharacterEncoding("utf-8");
            servletResponse.setCharacterEncoding("utf-8");
            servletResponse.setContentType("text/html;charset=utf-8");
            //讓請求繼續走下去,可能不止一個filter
            filterChain.doFilter(servletRequest,servletResponse);
        }
        //服務器關閉時執行
        public void destroy() {
            System.out.println("CharacterEnCodingFilter已經銷毀");
        }
    
  2. 在web.xml文件中配置filter

        <filter>
            <filter-name>charsetFilter</filter-name>
            <filter-class>com.iandf.filter.CharacterEnCodingFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>charsetFilter</filter-name>
            <!--只要是/servlet/....的請求就會經過這個過濾器-->
            <url-pattern>/servlet/*</url-pattern>
        </filter-mapping>
    

監聽器應用,實現登錄業務

用戶點擊登錄按鈕 -->判斷用戶名是否是admin->成功或者失敗的頁面
note:
判斷操作在loginservlet中完成,成功就往Session中保存一條數據並跳轉到success頁面,失敗跳轉到error頁面
不能直接訪問success頁面,使用過濾器完成

11 郵件發送和發送郵件注冊

1 郵件系統簡介

電子郵件系統最主要的組成構建

  1. 用戶代理(qq郵箱等電子郵件系統程序)
  2. 郵件服務器
  3. 電子郵件使用的協議 :smtp (simple mial transfer protocol) pop3 (post office protocol)郵局協議第三版

工作原理圖

image-20200814164915857

步驟:

①發信人調用用戶代理來撰寫和編輯要發送的郵件。用戶代理用SMTP把郵件傳送給發送方郵件服務器。
②發送方郵件服務器將郵件放入郵件緩存隊列中,等待發送。
③運行在發送方郵件服務器的SMTP客戶進程,發現郵件緩存中有待發送的郵件,就向運行在接收方郵件服務器的SMTP服務器進程發起建立TCP連接
④TCP連接建立后,SMTP客戶進程開始向遠程SMTP服務器進程發送郵件。當所有待發送郵件發完后,SMTP就關閉所建立的TCP連接。
⑤運行在接收方郵件服務器中的SMTP服務器進程收到郵件后,將郵件放入收信人的用戶郵箱,等待收信人在方便時進行讀取。
⑥收信人打算收信時,調用用戶代理,使用POP3(或IMAP)協議將自己的郵件從接收方郵件服務器的用戶郵箱中取回(如果郵箱中有來信的話)。

smtp協議用於發送(push)郵件,pop3協議用於拉(pull)郵件

用戶讀取郵件時,用戶代理向郵件服務器發送請求 ,拉取用戶郵箱中的郵件

2 簡單文本郵件代碼測試

jar包的支持:

  1. activation-1.1.1.jar
  2. mail-1.4.7.jar

簡易郵件的實現原理圖

image-20200814170527980

步驟如下:

  1. 創建定義整個應用程序所需要的環境信息的session對象
  2. 使用session對象創建transport對象
  3. 使用郵件用戶名和授權碼連上郵件服務器
  4. 創建郵件
    • 發送人,收件人,主題,要發送的文本
  5. 發送郵件,關閉連接

代碼如下

	    // write your code here
        Properties prop=new Properties();
        prop.setProperty("mail.host","smtp.qq.com");///設置QQ郵件SMTP服務器
        prop.setProperty("mail.transport.protocol","smtp");//郵件發送協議
        prop.setProperty("mail.smtp.auth","true");//需要驗證用戶密碼 authentication

        //QQ郵箱需要設置SSL加密
        MailSSLSocketFactory sf=new MailSSLSocketFactory();
        sf.setTrustAllHosts(true);
        prop.put("mail.smtp.ssl.enable","true");
        prop.put("mail.smtp.ssl.socketFactory",sf);
        //=========================================使用javaMail發送郵件的5個步驟===========================================
        //1.創建定義整個應用程序所需要的環境信息的session對象
        Session session = Session.getDefaultInstance(prop, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication("1289557927@qq.com","wjuumtponiowfgfg");
            }
        });
        //開啟session的debug模式,這樣可以查看到程序發送Email的運行狀態
        session.setDebug(true);
        //2.通過session得到transport對象
        Transport ts=session.getTransport();
        //3.使用郵箱的用戶名和授權碼連上郵件服務器 wjuumtponiowfgfg
        ts.connect("smtp.qq.com","1289557927@qq.com","wjuumtponiowfgfg");
        //4.創建郵件:寫文件
        //注意需要傳遞session
        MimeMessage message=new MimeMessage(session);
        //指明郵件的發件人
        message.setFrom(new InternetAddress("1289557927@qq.com"));
        //指明郵件的收件人
        message.setRecipient(Message.RecipientType.TO,new InternetAddress("1289557927@qq.com"));
        //郵件標題
        message.setSubject("發送的標題");
        //郵件的文本內容
        message.setContent("內容","text/html;charset=UTF-8");
        //5.發送郵件
        ts.sendMessage(message,message.getAllRecipients());
        //6.關閉連接
        ts.close();

測試結果

EHLO DESKTOP-16OBIHF
MAIL FROM:<1289557927@qq.com>
250 OK.
RCPT TO:<1289557927@qq.com>
250 OK
DEBUG SMTP: Verified Addresses
DEBUG SMTP:   1289557927@qq.com
DATA
354 End data with <CR><LF>.<CR><LF>.
From: 1289557927@qq.com
To: 1289557927@qq.com
Message-ID: <1073502961.0.1597397272807.JavaMail.IANDF@smtp.qq.com>
Subject: =?UTF-8?B?5Y+R6YCB55qE5qCH6aKY?=
MIME-Version: 1.0
Content-Type: text/html;charset=UTF-8
Content-Transfer-Encoding: base64

5YaF5a65
.
250 OK: queued as.
QUIT

3 MIME支持

多用途互聯網郵件擴展(英語:Multipurpose Internet Mail Extensions,縮寫:MIME)是一個互聯網標准,它擴展了電子郵件標准,使其能夠支持:

image-20200814205201762

每一個文本、圖片、附件可以分為一個MimeBodyPart,由MimeMultipart完成組裝

        Properties prop = new Properties();
        prop.setProperty("mail.host","smtp.qq.com");///設置QQ郵件服務器
        prop.setProperty("mail.transport.protocol","smtp");///郵件發送協議
        prop.setProperty("mail.smtp.auth","true");//需要驗證用戶密碼
        //QQ郵箱需要設置SSL加密
        MailSSLSocketFactory sf=new MailSSLSocketFactory();
        sf.setTrustAllHosts(true);
        prop.put("mail.smtp.ssl.enable","true");
        prop.put("mail.smtp.ssl.socketFactory",sf);

        //使用javaMail發送郵件的5個步驟
        //1.創建定義整個應用程序所需要的環境信息的session對象
        Session session=Session.getDefaultInstance(prop, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication("1289557927@qq.com","wjuumtponiowfgfg");
            }
        });
        //開啟session的debug模式,這樣可以查看到程序發送Email的運行狀態
        session.setDebug(true);
        //2.通過session得到transport對象
        Transport ts=session.getTransport();
        //3.使用郵箱的用戶名和授權碼連上郵件服務器
        ts.connect("smtp.qq.com","1289557927@qq.com","wjuumtponiowfgfg");
        //4.創建郵件:寫文件
        //注意需要傳遞session
        MimeMessage message=new MimeMessage(session);
        //指明郵件的發件人
        message.setFrom(new InternetAddress("1289557927@qq.com"));
        //指明郵件的收件人
        message.setRecipient(Message.RecipientType.TO,new InternetAddress("1289557927@qq.com"));
        //郵件標題
        message.setSubject("java發出");

        //郵件的文本內容
        //=================================准備圖片數據=======================================
        MimeBodyPart image=new MimeBodyPart();
        //圖片需要經過數據化的處理
        DataHandler dh=new DataHandler(new FileDataSource("D:\\ForStrong_java\\javaweb\\javaweb-08-sendMail\\src\\20200708003918960.png"));
        //在part中放入這個處理過圖片的數據
        image.setDataHandler(dh);
        //給這個part設置一個ID名字
        image.setContentID("bz.jpg");

        //准備正文的數據
        MimeBodyPart text=new MimeBodyPart();
        text.setContent("這是一張正文<img src='cid:bz.jpg'>","text/html;charset=UTF-8");

        //描述數據關系
        MimeMultipart mm=new MimeMultipart();
        mm.addBodyPart(text);
        mm.addBodyPart(image);
        mm.setSubType("related");

        //設置到消息中,保存修改
        message.setContent(mm);
        message.saveChanges();
        //5.發送郵件
        ts.sendMessage(message,message.getAllRecipients());

        //6.關閉連接
        ts.close();

12 文件上傳

使用的jar包:

  1. commons-fileupload-1.4.jar
  2. commons-io-2.6.jar

上傳文件到服務器的流程圖如下所示:

jsp文件

<form action="${pageContext.request.contextPath}/upload.do" method="post" enctype="multipart/form-data">
    上傳用戶:<input type="text" name="username"> <br>
    <p><input type="file" name="file1"></p>
    <p><input type="file" name="file2"></p>
    <p><input type="submit">|<input type="reset"></p>
</form>

servlet文件

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //判斷上傳的表單是普通表單還是帶文件的表單,是返回true,否返回false;
        if (!ServletFileUpload.isMultipartContent(request)){
            return;//如果這是一個普通文件我們直接返回
        }//如果通過了這個if,說明我們的表單是帶文件上傳的

        //創建上傳文件的保存目錄,為了安全建議在WEB-INF目錄下,用戶無法訪問
        String uploadpath = this.getServletContext().getRealPath("WEB-INF/Upload");//獲取上傳文件的保存路徑
        File uploadfile = new File(uploadpath);
        if (!uploadfile.exists()){
            uploadfile.mkdir();//如果目錄不存在就創建這樣一個目錄
        }

        //臨時文件
        //臨時路徑,如果上傳的文件超過預期的大小,我們將它存放到一個臨時目錄中,過幾天自動刪除,或者提醒用戶轉存為永久
        String tmppath = this.getServletContext().getRealPath("WEB-INF/tmp");
        File file = new File(tmppath);
        if (!file.exists()){
            file.mkdir();//如果目錄不存在就創建這樣臨時目錄
        }

        //處理上傳的文件一般需要通過流來獲取,我們可以通過request.getInputstream(),原生態文件上傳流獲取,十分麻煩
        //但是我們都建議使用Apache的文件上傳組件來實現,common-fileupload,它需要依賴於common-io組件;

        try {
            //1、創建DiskFileItemFactory對象,處理文件上傳路徑或限制文件大小
            DiskFileItemFactory factory = gteDiskFileItemFactory(file);
            //2、獲取ServletFileUpload
            ServletFileUpload upload = getServletFileUpload(factory);
            //3、處理上傳文件
            String msg = uploadParseRequest(upload,request,uploadpath);
            //Servlet請求轉發消息
            request.setAttribute("msg",msg);
            request.getRequestDispatcher("/info.jsp").forward(request,response);
        }catch (FileUploadException e){
            e.printStackTrace();
        }
    }
    public static DiskFileItemFactory gteDiskFileItemFactory(File file){
        //1、創建DiskFileItemFactory對象,處理文件上傳路徑或限制文件大小
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //通過這個工廠設置一個緩沖區,當上傳的文件大小大於緩沖區的時候,將它放到臨時文件中;
        factory.setSizeThreshold(1024 * 1024);//緩沖區大小為1M
        factory.setRepository(file);
        return factory;
    }
    public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory){
        //2、獲取ServletFileUpload
        ServletFileUpload upload = new ServletFileUpload(factory);
        //監聽文件上傳進度
        upload.setProgressListener((pBytesRead, lpContentLenght, i) -> {
            //pBytesRead:已讀取到的文件大小
            //pContentLenght:文件大小
            System.out.println("總大小:"+lpContentLenght+"已上傳:"+pBytesRead);
        });

        //處理亂碼問題
        upload.setHeaderEncoding("UTF-8");
        //設置單個文件的最大值
        upload.setFileSizeMax(1024 * 1024 * 10);
        //設置總共能夠上傳文件的大小
        //1024 = 1kb * 1024 = 1M * 10 = 10M
        upload.setSizeMax(1024 * 1024 * 10);
        return upload;
    }
    public static String uploadParseRequest(ServletFileUpload upload,HttpServletRequest request,String uploadpath) throws IOException, FileUploadException {
        String msg = "";
        //3、處理上傳文件
        //ServletFileUpload對象把前端的請求解析,封裝成一個FileItem對象
        List<FileItem> fileItems = upload.parseRequest(request);
        for (FileItem fileItem : fileItems) {
            if (fileItem.isFormField()){ //判斷是普通表單還是帶文件的表單
                //getFieldName指的是前端表單控件的name
                String name = fileItem.getFieldName();
                String value = fileItem.getString("UTF-8");//處理亂碼
                System.out.println(name+":"+value);
            }else {//判斷它是帶文件的表單

                //======================處理文件=======================//

                //拿到文件的名字
                String uploadFileName = fileItem.getName();
                System.out.println("上傳的文件名:"+uploadFileName);
                //返回一個字符串,其值為此字符串,並刪除任何前導和尾隨空格。
                if (uploadFileName.trim().equals("") || uploadFileName == null){
                    continue;
                }

                //獲得上傳的文件名,例如/img/girl/ooa.jpg,只需要ooa,其前面的后面的都不需要
                String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
                System.out.println("fileName:"+uploadFileName);
                //獲得文件的后綴名
                String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);
                      /*
                        如果文件后綴名fileExtName不是我們所需要的
                        就直接return,不處理,告訴用戶文件類型不對
                     */

                //可以使用UUID(唯一識別的通用碼),保證文件名唯一
                //UUID.randomUUID,隨機生一個唯一識別的通用碼

                //網絡傳輸中的東西,都需要序列化
                //pojo,實體類,如果想要在多個電腦運行,傳輸--->需要吧對象都序列化了
                //JNI=java Native Interface
                //implements Serializable :標記接口,JVM--->java棧 本地方法棧 native-->c++

                System.out.println("文件信息【文件名:"+fileName+"文件類型:"+fileExtName+"】");

                //可以使用UUID(唯一通用識別碼)來保證文件名的統一
                String uuidFileName = UUID.randomUUID().toString();


                //=======================傳輸文件=========================//
                //獲得文件上傳的流
                InputStream inputStream = fileItem.getInputStream();

                //創建一個文件輸出流
                FileOutputStream fos = new FileOutputStream(uploadpath + "/" + uuidFileName +"."+ fileExtName);

                //創建一個緩沖區
                byte[] buffer = new byte[1024 * 1024];

                //判斷是否讀取完畢
                int len = 0;

                //如果大於0,說明還存在數據
                while ((len=inputStream.read(buffer))>0){
                    fos.write(buffer,0,len);
                }
                //關閉流
                fos.close();
                inputStream.close();
                msg = "文件上傳成功!";
                fileItem.delete();//上傳成功,清除臨時文件
            }
        }

        return msg;
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM