Java Servlet 實踐篇

轉載自http://blog.csdn.net/justloveyou_/

摘要:

  伴隨 J2EE 6一起發佈的Servlet 3.0規範是Servlet規範歷史上最重要的變革之一,它的許多新的特性都極大的簡化了 Java Web 應用的開發。本文從一個簡單的 Servlet 例子開始,說明了如何開發、配置一個 Servlet。此外,還重點敘述了Servlet的一些新特性,包括Servlet 異步處理、Servlet 非阻塞IO 以及 Servlet 文件上傳等內容,以便我們對Servlet有一個更全面的瞭解。


  本篇主要介紹 Servlet 實踐方面的知識,更多關注於Servlet的新特性:

  • Servlet 實例;
  • Servlet 配置;
  • Servlet 異步處理;
  • Servlet 非阻塞IO;
  • Servlet 文件上傳。

  更多關於Servlet理論方面的介紹見我的上一篇博文《Servlet 綜述(理論篇)》,其具體包括以下幾個方面的內容:

  • 爲什麼會有 Servlet;
  • Servlet 是什麼;
  • Servlet 如何實現預期效果;
  • Servlet 的作用原理;
  • Servlet 與 併發;
  • Servlet 與 Java Web 應用的結構演變歷程;
  • Servlet 與 MVC 的聯繫。

一. 從一個簡單的 Servlet 例子說起

  我們看下面這個簡單的示例:

Servlet:

public class TestServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {

        //獲取請求參數
        String param1 = request.getParameter("name");
        String param2 = request.getParameter("gentle");

        //獲取Servlet參數並放到request中
        String age = this.getServletConfig().getInitParameter("age");
        request.setAttribute("age",age);


        // 此處進行業務邏輯處理


        //根據處理結果轉發到相應的表現層進行顯示
        request.getRequestDispatcher("/showInfo.jsp").forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doGet(req, resp);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

web.xml 配置文件片段:

<context-param>
    <param-name>campus</param-name>
    <param-value>NEU</param-value>
</context-param>
<servlet>
    <servlet-name>TestServlet</servlet-name>
    <servlet-class>com.edu.tju.rico.servlet.TestServlet</servlet-class>
    <init-param>
        <param-name>age</param-name>
        <param-value>24</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>TestServlet</servlet-name>
    <url-pattern>/servlet/test</url-pattern>
</servlet-mapping>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

顯示邏輯:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<html>
<head>
<title>showInfo</title>
</head>
<body>
    請求參數: Name:&nbsp;&nbsp;<%= request.getParameter("name")%><br>
    Gentle:&nbsp;&nbsp;<%= request.getParameter("gentle")%><br>
    <br> 
    ----------------我是分割線--------------------<br> 
    <br> 
    Web應用初始化參數: &nbsp;&nbsp;<%= application.getInitParameter("campus")%><br>
    <br> 
    ----------------我是分割線--------------------<br> 
    <br> 
    TestServlet 初始化參數: &nbsp;&nbsp;${requestScope.age}<br>
    <br>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

  開發一個Servlet程序時,如果其是基於HTTP協議的,那麼我們一般繼承 HttPServlet 抽象類並重寫 doGet() 和 doPost() 方法,或者直接重寫 service() 方法去處理Http請求。

  更多關於 JSP技術的細節見我的其他兩篇博客: 《Java Web基礎 — Jsp 綜述(上)》 和 《Java Web基礎 — Jsp 綜述(下)》


二. Servlet 的配置

  爲了讓 Servlet 能夠響應用戶請求,還必須將 Servlet 配置到我們的Web應用中。進一步地,如果我們沒有爲Servlet配置URL,那麼該Servlet將不能響應用戶請求。從 J2EE 6 (Servlet 3.0) 開始,配置 Servlet 的方式共有兩種:

  • 在 web.xml 中進行配置;

  • 在對應的Servlet類中使用@WebServlet註解進行配置。


  我們在這裏主要說明使用@WebServlet註解進行配置Servlet,使用 web.xml 配置的方法與該種方式只是在形式不同,作用方式是一樣的,此不贅述。

  一旦我們使用 @WebServlet 配置了Servlet,那我們就不用在 web.xml 進行再次配置了,並且不能在web.xml中將 metadata-complete 屬性設置爲true。 支持的常用屬性如下表所示:

屬性名 是否必需 類型 描述
name String 指定 Servlet 的 name 屬性,等價於<servlet-name>標籤,默認取值爲Servlet類的全限定名
value String[] 該屬性等價於 urlPatterns 屬性,這兩個屬性不能同時使用
loadOnStartup int 指定Servlet的加載時機和順序,等價於<load-on-startup>標籤
initParam WebInitParam[] 指定一組 Servlet 初始化參數,等價於<init-param>標籤
asyncSupported boolean 聲明 Servlet 是否支持異步操作模式,等價於<async-supported>標籤
description String 該Servlet的描述信息,等價於<description>標籤
displayName String 該Servlet的顯示名,通常配合工具使用,等價於<display-name>標籤

  將上述示例使用@WebServlet註解配置,如下:

@WebServlet(name = "Test", 
    urlPatterns = { "/servlet/test" }, 
    initParams = { @WebInitParam(name = "age", value = "24") })
public class TestServlet extends HttpServlet {
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

三. Servlet的新特性:異步處理

1、Servlet 不支持異步處理會帶來哪些痛點?

  在本篇的姊妹篇《Java Web基礎 — Servlet 綜述(實踐篇)》中,我們已經提到Servlet容器處理請求的方式。對於每個到達Web容器的請求,Web容器會爲其分配一條執行線程來專門負責該請求,直到迴應完成前,該執行線程都不會被釋放回Web容器的線程池。 我們知道,執行線程會耗用系統資源,若某些請求需要長時間處理(例如長時間運算、等待某個資源),就會長時間佔用執行線程,若這類的請求很多,許多執行線程都被長時間佔用,對於整個系統而言就會是個性能負擔,甚至造成應用的性能瓶頸。

  特別地,基本上一些需長時間處理的請求,通常客戶端也較不在乎請求後要有立即的迴應,若可以,讓這類請求先釋放容器分配給該請求的執行線程,讓容器可以有機會將執行線程資源分配給其它的請求,這樣可以減輕系統負擔。這樣,原先釋放了容器所分配執行線程的請求,其迴應將被延後,直到處理完成(例如長時間運算完成、所需資源已獲得)再行對客戶端的迴應。


2、如何使用 Servlet 3.0 去支持對耗時事務的異步處理?

1)、Servlet 3.0 對異步處理的支持

  在 Servlet 3.0 之前的規範中,如果Servlet作爲控制器調用了一個耗時的業務方法,那麼 Servlet 必須等到業務方法完全返回之後才能生成響應,這使得 Servlet 對業務方法的調用是一種阻塞式調用,因此效率比較低。Servlet 3.0 規範引入了異步處理來解決問題,異步處理允許Servlet重新發起一個線程去調用耗時的業務方法,這樣就可以避免等待。

  Servlet 3.0 的異步處理是通過 AsyncContext 類來處理的,Servlet 可以通過 ServletRequest 的如下兩個方法開啓異步調用、創建 AsyncContext 對象。在這裏,AsyncContext 對象代表異步處理的上下文。

  • AsyncContext startAsync()
  • AsyncContext startAsync(ServletRequest,ServletRequest)

  這兩個方法都會返回 AsyncContext 對象,前者會直接利用原有的請求與響應對象來創建AsyncContext對象,後者則允許你傳入自行創建的請求、響應對象。 在調用了startAsync()方法取得AsyncContext對象之後,這次的響應就會被延後,並釋放容器所分配的執行線程。

  我們可以通過AsyncContext的getRequest()、 getResponse()方法取得請求、響應對象,此次對客戶端的響應將暫緩至調用AsyncContext的complete()方法或dispatch()方法爲止,前者表示響應完成,後者表示將調用指定URL對應的內容進行響應。特別需要注意的是,dispatch()前後仍是同一個請求,並且被異步請求dispatch的目標頁面必須指定:session=”false”。如果我們要支持 Servlet 的異步處理,我們的 Servlet 就必須能夠支持非同步處理。也就是說,如果我們使用@WebServlet來標註的話,則必須將其asyncSupported屬性設爲true,如下所示:

@WebServlet(urlPatterns = "/some.do", asyncSupported = true )   
public class AsyncServlet extends HttpServlet {   
...  
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

  特別需要注意的是,如果Servlet將支持非同步處理,並且其前端有過濾器,那麼過濾器也必須表明其支持非同步處理,如果使用@WebFilter註解的方式,同樣是需要設定其asyncSupported屬性爲true,如下所示:

@WebFilter(urlPatterns = "/some.do", asyncSupported = true )   
public class AsyncFilter implements Filter{   
...
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

2)、異步處理實例

(1) 進行異步處理的Servlet類:

@WebServlet(urlPatterns = "/async",asyncSupported=true )
public class AsyncServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        request.setAttribute("param1", "我在異步處理前被設置...");
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println("<title>異步調用示例</title>");
        out.println("進入Servlet的時間:" + new java.util.Date() + ".<br/>");

        //創建AsyncContext對象,開始異步調用
        final AsyncContext async = request.startAsync();  // 在局部(匿名)內部類直接使用,必須設爲 final

        // 設置異步調用的請求時長
        async.setTimeout(10 * 1000);

        // 啓動線程去處理耗時任務
        async.start(new Runnable() {   // 匿名內部類

            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    HttpServletRequest req = (HttpServletRequest) async
                            .getRequest();
                    req.setAttribute("param2", "我在耗時任務處理線程中被設置...");
                    async.dispatch("/async.jsp");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        out.println("結束Servlet的時間:" + new java.util.Date() + ".<br/>");
        out.flush();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doGet(req, resp);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

(2) 表現層:

<%-- 被異步請求dispatch的目標頁面必須指定:session="false" --%>
<%@ page contentType="text/html; charset=utf-8" language="java" session="false"%>
<div style="background-color:#ffffdd;height:80px;">
    param1:${param1}<br />
    param2:${param2}<br />

    <%
        out.println("業務調用結束的時間:" + new java.util.Date());
    %>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

(3) 結果展示頁面

          Servlet異步處理.png-33.6kB


四、Servlet 3.1 支持非阻塞 IO

1、Servlet 不支持非阻塞 IO會帶來哪些痛點?

  Servlet 3.0 允許異步請求處理,但僅限於傳統I/O,這大大限制了程序的可擴展性。我們知道,在應用程序中,一種典型的做法是,通過while循環讀取Servlet輸入流(Servlet InputStream),如下所示:

public class TestServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
         throws IOException, ServletException {     
 ServletInputStream input = request.getInputStream();
       byte[] b = new byte[1024];
       int len = -1;
       while ((len = input.read(b)) != -1) {
          . . . 
       }
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

   
  事實上,Servlet 底層的 IO 是通過以下兩個 IO 流支撐的:

  • ServletInputStream:Servlet 用於讀取數據的輸入流;

  • ServletOutputStream:Servlet 用於輸出數據的輸出流。


  以 Servlet 讀取數據爲例,傳統的讀取方式採用阻塞式IO —— 當Servlet讀取瀏覽器提交的數據時,如果數據暫時不可用,或者數據沒有讀取完成,Servlet當前所有線程將會被阻塞,無法繼續執行下去。另外,如果傳入的數據受到阻塞或流傳輸的速度慢於服務器讀取的速度,則服務器線程就需要一直等待數據的到來。同樣的情形在向Servlet輸出流(Servlet OutputStream)寫數據的時候也會出現。Servlet 3.1 提供的非阻塞IO進行輸入、輸出,可以更好地提升性能。


2、如何使用 Servlet 3.0 去支持非阻塞 IO?

  上述問題可以通過添加Servlet 3.1(JSR 340,作爲Java EE7發佈的一部分)提供的事件監聽器ReadListener和WriteListener接口進行解決。通過 ServletInputStream.setReadListener 和 ServletOutputStream.setWriteListener 可以註冊監聽器。監聽器中提供了一些回調方法,在數據讀寫不受阻塞的時候進行觸發。以 ReadListener 爲例,實現ReadListener事件監聽器需要實現如下三個方法:

  • onDataAvailable():當有數據可用時激發該方法;

  • onAllDataRead():當所有數據讀取完成時激發該方法;

  • onError(Throwable t):讀取數據出現錯誤時激發該方法。


1)、Servlet 3.1 使用非阻塞IO步驟

  在 Servlet 3.1 中使用非阻塞IO步驟可分爲三步:

(1) 調用 ServletRequest 的startAsync()方法開啓異步處理模式; 
(2) 通過 ServletRequest 獲取 ServletInputStream,併爲 ServletInputStream 設置監聽器(ReadListener 實現類); 
(3) 實現 ReadListener 接口來實現監聽器,在該監聽器的方法中以非阻塞方式讀取數據。

  改進後的doGet方法如下所示:

AsyncContext context = request.startAsync();
ServletInputStream input = request.getInputStream();
input.setReadListener(new MyReadListener(input, context));
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

  setXXXListner方法指出採用非阻塞I/O而不是傳統I/O。onReadListener可以通過ServletInputStream進行註冊,同樣地,oneWritelistener可以通過ServletOutputStream進行註冊。特別地,新增加的ServletInputStream.isReady方法和ServletInputStream.isFinished方法用於檢測非阻塞I/O的讀取狀態,而ServletOutputStream.canWrite方法用於檢測數據是否能夠無阻塞地寫入。


2)、Servlet 3.1 使用非阻塞IO實例

(1) 請求提交表單 form.html:

<html>
<head>
    <meta name="author" content="Yeeku.H.Lee(CrazyIt.org)" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>  </title>
</head>
<body>
<form action="asyn" method="post">
    用戶名:<input type="text" name="name"/><br/>
    密碼:<input type="text" name="pass"/><br/>
    <input type="submit" value="提交">
    <input type="reset" value="重設">
</form>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

(2) 在 Servlet中使用非阻塞IO:

@WebServlet(urlPatterns = "/async",asyncSupported=true )
public class AsyncServlet extends HttpServlet {

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        request.setAttribute("param1", "我在異步處理前被設置...");
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println("<title>非阻塞IO示例</title>");
        out.println("進入Servlet的時間:" + new java.util.Date() + ".<br/>");

        //創建AsyncContext對象,開始異步調用
        final AsyncContext async = request.startAsync();

        // 設置異步調用的請求時長
        async.setTimeout(10 * 1000);
        final ServletInputStream input = request.getInputStream();

        // 爲輸入流注冊監聽器
        input.setReadListener(new ReadListener() {

            @Override
            public void onError(Throwable t) {
                t.printStackTrace();
            }

            @Override
            public void onDataAvailable() throws IOException {
                System.out.println("數據可用!!");
                try
                {
                    // 暫停5秒,模擬讀取數據是一個耗時操作。
                    Thread.sleep(5000);
                    StringBuilder sb = new StringBuilder();
                    int len = -1;
                    byte[] buff = new byte[1024];
                    // 採用原始IO方式讀取瀏覽器向Servlet提交的數據
                    while (input.isReady() && (len = input.read(buff)) > 0)
                    {
                        String data = new String(buff , 0 , len);
                        sb.append(data);
                    }
                    System.out.println(sb);
                    // 將數據設置爲request範圍的屬性
                    async.getRequest().setAttribute("data" , sb.toString());
                    // 轉發到視圖頁面
                    async.dispatch("/asyn.jsp");
                }
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }
            }

            @Override
            public void onAllDataRead() throws IOException {
                System.out.println("數據讀取完成");
            }
        });

        out.println("結束Servlet的時間:" + new java.util.Date() + ".<br/>");
        out.flush();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doGet(req, resp);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

(3) 表現層:

<%@ page contentType="text/html; charset=utf-8" language="java" session="false"%>
<div style="background-color:#ffffdd;height:80px;">
瀏覽器提交數據爲:${data}<br/>
<%=new java.util.Date()%>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

(4) 結果展示:

              nio.png-14.3kB

  更多關於Servlet使用、實踐方面的介紹以及Servlet新特性的總結見我的下一篇博文《Servlet 綜述(實踐篇)》。


五、Servlet 3.0 支持文件上傳

  Servlet 3.0之前的版本中,文件上傳是個挺讓人頭疼的問題,雖然有第三方框架(Apache Commons)來實現,但使用起來還是比較麻煩。在Servlet 3.0中,這些問題將不復存在,Servlet 3.0對文件上傳提供了直接支持,配合Servlet 3.0中基於Annotations的配置,大大簡化上傳件的操作。

  在使用表單上傳文件時,我們需要使用@MultipartConfig註解去修飾對應的Servlet。此外,我們一方面需要在表單裏使用<input type=”file” …/>文件域,另一方面必須要爲表單域設置 enctype 屬性,其有三個值:

  • application/x-www-form-urlencoded:表單數據被編碼爲名稱/值對,這是默認的編碼方式;
  • multipart/form-data:以二進制流的方式處理表單數據,一般用於傳輸二進制文件,如圖片、視頻等;
  • text/plain:不編碼特殊字符,適用於通過表單發送郵件。

下面跟進這個例子來體會其給我們帶來的便捷。

(1) 文件提交表單:

<%@ page contentType="text/html; charset=utf-8" language="java" errorPage="" %>
<head>
    <title> 文件上傳 </title>
</head>
<body>
<form method="post" action="upload"  enctype="multipart/form-data">
    文件名:<input type="text" id="name" name="name" /><br/>
    選擇文件:<input type="file" id="file" name="file" /><br/>
    <input type="submit" value="上傳" /><br/>
</form>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

(2) 文件上傳Servlet:

@WebServlet(name="upload" , urlPatterns={"/upload"})
@MultipartConfig
public class UploadServlet extends HttpServlet
{
    public void service(HttpServletRequest request ,
        HttpServletResponse response)
        throws IOException , ServletException
    {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        request.setCharacterEncoding("utf-8");
        // 獲取普通請求參數
        String name = request.getParameter("name");
        out.println("普通的name參數爲:" + name + "<br/>");
        // 獲取文件上傳域
        Part part = request.getPart("file");
        // 獲取上傳文件的文件類型
        out.println("上傳文件的的類型爲:"
            + part.getContentType() + "<br/>");
        //獲取上傳文件的大小。
        out.println("上傳文件的的大小爲:" + part.getSize()  + "<br/>");
        // 獲取該文件上傳域的Header Name
        Collection<String> headerNames = part.getHeaderNames();
        // 遍歷文件上傳域的Header Name、Value
        for (String headerName : headerNames)
        {
            out.println(headerName + "--->"
                + part.getHeader(headerName) + "<br/>");
        }
        // 獲取包含原始文件名的字符串
        String fileNameInfo = part.getHeader("content-disposition");
        // 提取上傳文件的原始文件名
        String fileName = fileNameInfo.substring(
            fileNameInfo.indexOf("filename=\"") + 10 , fileNameInfo.length() - 1);
        // 將上傳的文件寫入服務器
        part.write(getServletContext().getRealPath("/uploadFiles")
            + "/" + fileName );               // ①

        System.out.println(getServletContext().getRealPath("/uploadFiles"));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

(3) 結果展示:

            fileupload.png-11kB


六、拾遺增補

  除上面提到的內容,Servlet 還引入了其他新的特性(如下所述),此不贅述。

  • Servlet 3.0 爲 Web 模塊化提供了支持;
  • Servlet 3.1 可以強制更改 Session ID,具體由 HttpServletRequest 的 changeSessionId()方法完成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章