1. 程式人生 > >javaweb學習總結(七)——HttpServletResponse物件(一)

javaweb學習總結(七)——HttpServletResponse物件(一)

    Web伺服器收到客戶端的http請求,會針對每一次請求,分別建立一個用於代表請求的request物件、和代表響應的response物件。request和response物件即然代表請求和響應,那我們要獲取客戶機提交過來的資料,只需要找request物件就行了。要向客戶機輸出資料,只需要找response物件就行了。

一、HttpServletResponse物件介紹

      HttpServletResponse物件代表伺服器的響應。這個物件中封裝了向客戶端傳送資料、傳送響應頭,傳送響應狀態碼的方法。檢視HttpServletResponse的API,可以看到這些相關的方法。

1.1、負責向客戶端(瀏覽器)傳送資料的相關方法      

1.2、負責向客戶端(瀏覽器)傳送響應頭的相關方法

 

  

1.3、負責向客戶端(瀏覽器)傳送響應狀態碼的相關方法

1.4、響應狀態碼的常量

  HttpServletResponse定義了很多狀態碼的常量(具體可以檢視Servlet的API),當需要向客戶端傳送響應狀態碼時,可以使用這些常量,避免了直接寫數字,常見的狀態碼對應的常量:

 狀態碼404對應的常量

  

  狀態碼200對應的常量

  

  狀態碼500對應的常量

二、HttpServletResponse物件常見應用

 2.1、使用OutputStream流向客戶端瀏覽器輸出中文資料

        使用OutputStream流輸出中文注意問題:

    在伺服器端,資料是以哪個碼錶輸出的,那麼就要控制客戶端瀏覽器以相應的碼錶開啟,比如:outputStream.write("中國".getBytes("UTF-8"));使用OutputStream流向客戶端瀏覽器輸出中文,以UTF-8的編碼進行輸出,此時就要控制客戶端瀏覽器以UTF-8的編碼開啟,否則顯示的時候就會出現中文亂碼,那麼在伺服器端如何控制客戶端瀏覽器以以UTF-8的編碼顯示資料呢?可以通過設定響應頭控制瀏覽器的行為,例如:response.setHeader("content-type", "text/html;charset=UTF-8");通過設定響應頭控制瀏覽器以UTF-8的編碼顯示資料。

範例:使用OutputStream流向客戶端瀏覽器輸出"中國"這兩個漢字

package gacl.response.study;

import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ResponseDemo01 extends HttpServlet {

    private static final long serialVersionUID = 4312868947607181532L;

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        outputChineseByOutputStream(response);//使用OutputStream流輸出中文
    }
    
    /**
     * 使用OutputStream流輸出中文
     * @param request
     * @param response
     * @throws IOException 
     */
    public void outputChineseByOutputStream(HttpServletResponse response) throws IOException{
        /**使用OutputStream輸出中文注意問題:
         * 在伺服器端,資料是以哪個碼錶輸出的,那麼就要控制客戶端瀏覽器以相應的碼錶開啟,
         * 比如:outputStream.write("中國".getBytes("UTF-8"));//使用OutputStream流向客戶端瀏覽器輸出中文,以UTF-8的編碼進行輸出
         * 此時就要控制客戶端瀏覽器以UTF-8的編碼開啟,否則顯示的時候就會出現中文亂碼,那麼在伺服器端如何控制客戶端瀏覽器以以UTF-8的編碼顯示資料呢?
         * 可以通過設定響應頭控制瀏覽器的行為,例如:
         * response.setHeader("content-type", "text/html;charset=UTF-8");//通過設定響應頭控制瀏覽器以UTF-8的編碼顯示資料
         */
        String data = "中國";
        OutputStream outputStream = response.getOutputStream();//獲取OutputStream輸出流
        response.setHeader("content-type", "text/html;charset=UTF-8");//通過設定響應頭控制瀏覽器以UTF-8的編碼顯示資料,如果不加這句話,那麼瀏覽器顯示的將是亂碼
        /**
         * data.getBytes()是一個將字元轉換成位元組陣列的過程,這個過程中一定會去查碼錶,
         * 如果是中文的作業系統環境,預設就是查詢查GB2312的碼錶,
         * 將字元轉換成位元組陣列的過程就是將中文字元轉換成GB2312的碼錶上對應的數字
         * 比如: "中"在GB2312的碼錶上對應的數字是98
         *         "國"在GB2312的碼錶上對應的數字是99
         */
        /**
         * getBytes()方法如果不帶引數,那麼就會根據作業系統的語言環境來選擇轉換碼錶,如果是中文作業系統,那麼就使用GB2312的碼錶
         */
        byte[] dataByteArr = data.getBytes("UTF-8");//將字元轉換成位元組陣列,指定以UTF-8編碼進行轉換
        outputStream.write(dataByteArr);//使用OutputStream流向客戶端輸出位元組陣列
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

 執行結果如下:

  客戶端瀏覽器接收到資料後,就按照響應頭上設定的字元編碼來解析資料,如下所示:

2.2、使用PrintWriter流向客戶端瀏覽器輸出中文資料

使用PrintWriter流輸出中文注意問題:

  在獲取PrintWriter輸出流之前首先使用"response.setCharacterEncoding(charset)"設定字元以什麼樣的編碼輸出到瀏覽器,如:response.setCharacterEncoding("UTF-8");設定將字元以"UTF-8"編碼輸出到客戶端瀏覽器,然後再使用response.getWriter();獲取PrintWriter輸出流,這兩個步驟不能顛倒,如下:

 response.setCharacterEncoding("UTF-8");//設定將字元以"UTF-8"編碼輸出到客戶端瀏覽器
 /**
 * PrintWriter out = response.getWriter();這句程式碼必須放在response.setCharacterEncoding("UTF-8");之後
 * 否則response.setCharacterEncoding("UTF-8")這行程式碼的設定將無效,瀏覽器顯示的時候還是亂碼
 */
 PrintWriter out = response.getWriter();//獲取PrintWriter輸出流

  然後再使用response.setHeader("content-type", "text/html;charset=字元編碼");設定響應頭,控制瀏覽器以指定的字元編碼編碼進行顯示,例如:

 //通過設定響應頭控制瀏覽器以UTF-8的編碼顯示資料,如果不加這句話,那麼瀏覽器顯示的將是亂碼
 response.setHeader("content-type", "text/html;charset=UTF-8");

     除了可以使用response.setHeader("content-type", "text/html;charset=字元編碼");設定響應頭來控制瀏覽器以指定的字元編碼編碼進行顯示這種方式之外,還可以用如下的方式來模擬響應頭的作用

 /**
 * 多學一招:使用HTML語言裡面的<meta>標籤來控制瀏覽器行為,模擬通過設定響應頭控制瀏覽器行為
  *response.getWriter().write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");
 * 等同於response.setHeader("content-type", "text/html;charset=UTF-8");
 */
 response.getWriter().write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");

範例:使用PrintWriter流向客戶端瀏覽器輸出"中國"這兩個漢字

package gacl.response.study;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ResponseDemo01 extends HttpServlet {

    private static final long serialVersionUID = 4312868947607181532L;

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        outputChineseByPrintWriter(response);//使用PrintWriter流輸出中文
    }

    /**
     * 使用PrintWriter流輸出中文
     * @param request
     * @param response
     * @throws IOException 
     */
    public void outputChineseByPrintWriter(HttpServletResponse response) throws IOException{
        String data = "中國";
        
        //通過設定響應頭控制瀏覽器以UTF-8的編碼顯示資料,如果不加這句話,那麼瀏覽器顯示的將是亂碼
        //response.setHeader("content-type", "text/html;charset=UTF-8");
        
        response.setCharacterEncoding("UTF-8");//設定將字元以"UTF-8"編碼輸出到客戶端瀏覽器
        /**
         * PrintWriter out = response.getWriter();這句程式碼必須放在response.setCharacterEncoding("UTF-8");之後
         * 否則response.setCharacterEncoding("UTF-8")這行程式碼的設定將無效,瀏覽器顯示的時候還是亂碼
         */
        PrintWriter out = response.getWriter();//獲取PrintWriter輸出流
        /**
         * 多學一招:使用HTML語言裡面的<meta>標籤來控制瀏覽器行為,模擬通過設定響應頭控制瀏覽器行為
         * out.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");
         * 等同於response.setHeader("content-type", "text/html;charset=UTF-8");
         */
        out.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");
        out.write(data);//使用PrintWriter流向客戶端輸出字元
    }
    
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

   當需要向瀏覽器輸出字元資料時,使用PrintWriter比較方便,省去了將字元轉換成位元組陣列那一步。

2.3、使用OutputStream或者PrintWriter向客戶端瀏覽器輸出數字

比如有如下的程式碼:

package gacl.response.study;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ResponseDemo01 extends HttpServlet {

    private static final long serialVersionUID = 4312868947607181532L;

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        outputOneByOutputStream(response);//使用OutputStream輸出1到客戶端瀏覽器
        
    }

    /**
     * 使用OutputStream流輸出數字1
     * @param request
     * @param response
     * @throws IOException 
     */
    public void outputOneByOutputStream(HttpServletResponse response) throws IOException{
        response.setHeader("content-type", "text/html;charset=UTF-8");
        OutputStream outputStream = response.getOutputStream();
        outputStream.write("使用OutputStream流輸出數字1:".getBytes("UTF-8"));
        outputStream.write(1);
    }
    
}

  執行上面程式碼顯示的結果如下:

  執行的結果和我們想象中的不一樣,數字1沒有輸出來,下面我們修改一下上面的outputOneByOutputStream方法的程式碼,修改後的程式碼如下:

/**
     * 使用OutputStream流輸出數字1
     * @param request
     * @param response
     * @throws IOException 
     */
    public void outputOneByOutputStream(HttpServletResponse response) throws IOException{
        response.setHeader("content-type", "text/html;charset=UTF-8");
        OutputStream outputStream = response.getOutputStream();
        outputStream.write("使用OutputStream流輸出數字1:".getBytes("UTF-8"));
        //outputStream.write(1);
        outputStream.write((1+"").getBytes());
    }

   1+""這一步是將數字1和一個空字串相加,這樣處理之後,數字1就變成了字串1了,然後再將字串1轉換成位元組陣列使用OutputStream進行輸出,此時看到的結果如下:

  這次可以看到輸出來的1了,這說明了一個問題:在開發過程中,如果希望伺服器輸出什麼瀏覽器就能看到什麼,那麼在伺服器端都要以字串的形式進行輸出

  如果使用PrintWriter流輸出數字,那麼也要先將數字轉換成字串後再輸出,如下:

/**
     * 使用PrintWriter流輸出數字1
     * @param request
     * @param response
     * @throws IOException 
     */
    public void outputOneByPrintWriter(HttpServletResponse response) throws IOException{
        response.setHeader("content-type", "text/html;charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();//獲取PrintWriter輸出流
        out.write("使用PrintWriter流輸出數字1:");
        out.write(1+"");
    }

 2.4、檔案下載

檔案下載功能是web開發中經常使用到的功能,使用HttpServletResponse物件就可以實現檔案的下載

檔案下載功能的實現思路:

  1.獲取要下載的檔案的絕對路徑

  2.獲取要下載的檔名

  3.設定content-disposition響應頭控制瀏覽器以下載的形式開啟檔案

  4.獲取要下載的檔案輸入流

  5.建立資料緩衝區

  6.通過response物件獲取OutputStream流

  7.將FileInputStream流寫入到buffer緩衝區

  8.使用OutputStream將緩衝區的資料輸出到客戶端瀏覽器

範例:使用Response實現檔案下載

package gacl.response.study;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @author gacl
 * 檔案下載
 */
public class ResponseDemo02 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        downloadFileByOutputStream(response);//下載檔案,通過OutputStream流
    }

    /**
     * 下載檔案,通過OutputStream流
     * @param response
     * @throws FileNotFoundException
     * @throws IOException
     */
    private void downloadFileByOutputStream(HttpServletResponse response)
            throws FileNotFoundException, IOException {
        //1.獲取要下載的檔案的絕對路徑
        String realPath = this.getServletContext().getRealPath("/download/1.JPG");
        //2.獲取要下載的檔名
        String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);
        //3.設定content-disposition響應頭控制瀏覽器以下載的形式開啟檔案
        response.setHeader("content-disposition", "attachment;filename="+fileName);
        //4.獲取要下載的檔案輸入流
        InputStream in = new FileInputStream(realPath);
        int len = 0;
        //5.建立資料緩衝區
        byte[] buffer = new byte[1024];
        //6.通過response物件獲取OutputStream流
        OutputStream out = response.getOutputStream();
        //7.將FileInputStream流寫入到buffer緩衝區
        while ((len = in.read(buffer)) > 0) {
        //8.使用OutputStream將緩衝區的資料輸出到客戶端瀏覽器
            out.write(buffer,0,len);
        }
        in.close();
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

執行結果如下所示:

 範例:使用Response實現中文檔案下載

  下載中文檔案時,需要注意的地方就是中文檔名要使用URLEncoder.encode方法進行編碼(URLEncoder.encode(fileName, "字元編碼")),否則會出現檔名亂碼。

package gacl.response.study;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @author gacl
 * 檔案下載
 */
public class ResponseDemo02 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        downloadChineseFileByOutputStream(response);//下載中文檔案
    }

    /**
     * 下載中文檔案,中文檔案下載時,檔名要經過URL編碼,否則會出現檔名亂碼
     * @param response
     * @throws FileNotFoundException
     * @throws IOException
     */
    private void downloadChineseFileByOutputStream(HttpServletResponse response)
            throws FileNotFoundException, IOException {
        String realPath = this.getServletContext().getRealPath("/download/張家界國家森林公園.JPG");//獲取要下載的檔案的絕對路徑
        String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);//獲取要下載的檔名
        //設定content-disposition響應頭控制瀏覽器以下載的形式開啟檔案,中文檔名要使用URLEncoder.encode方法進行編碼,否則會出現檔名亂碼
        response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
        InputStream in = new FileInputStream(realPath);//獲取檔案輸入流
        int len = 0;
        byte[] buffer = new byte[1024];
        OutputStream out = response.getOutputStream();
        while ((len = in.read(buffer)) > 0) {
            out.write(buffer,0,len);//將緩衝區的資料輸出到客戶端瀏覽器
        }
        in.close();
    }
    
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

執行結果如下所示:

  檔案下載注意事項:編寫檔案下載功能時推薦使用OutputStream流,避免使用PrintWriter流,因為OutputStream流是位元組流,可以處理任意型別的資料,而PrintWriter流是字元流,只能處理字元資料,如果用字元流處理位元組資料,會導致資料丟失。

範例:使用PrintWriter流下載檔案

package gacl.response.study;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @author gacl
 * 檔案下載
 */
public class ResponseDemo02 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        downloadFileByPrintWriter(response);//下載檔案,通過PrintWriter流
    }

    /**
     * 下載檔案,通過PrintWriter流,雖然也能夠實現下載,但是會導致資料丟失,因此不推薦使用PrintWriter流下載檔案
     * @param response
     * @throws FileNotFoundException
     * @throws IOException
     */
    private void downloadFileByPrintWriter(HttpServletResponse response)
            throws FileNotFoundException, IOException {
        String realPath = this.getServletContext().getRealPath("/download/張家界國家森林公園.JPG");//獲取要下載的檔案的絕對路徑
        String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);//獲取要下載的檔名
        //設定content-disposition響應頭控制瀏覽器以下載的形式開啟檔案,中文檔名要使用URLEncoder.encode方法進行編碼
        response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
        FileReader in = new FileReader(realPath);
        int len = 0;
        char[] buffer = new char[1024];
        PrintWriter out = response.getWriter();
        while ((len = in.read(buffer)) > 0) {
            out.write(buffer,0,len);//將緩衝區的資料輸出到客戶端瀏覽器
        }
        in.close();
    }
    
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}package gacl.response.study;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @author gacl
 * 檔案下載
 */
public class ResponseDemo02 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        downloadFileByPrintWriter(response);//下載檔案,通過PrintWriter流
    }

    /**
     * 下載檔案,通過PrintWriter流,雖然也能夠實現下載,但是會導致資料丟失,因此不推薦使用PrintWriter流下載檔案
     * @param response
     * @throws FileNotFoundException
     * @throws IOException
     */
    private void downloadFileByPrintWriter(HttpServletResponse response)
            throws FileNotFoundException, IOException {
        String realPath = this.getServletContext().getRealPath("/download/張家界國家森林公園.JPG");//獲取要下載的檔案的絕對路徑
        String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);//獲取要下載的檔名
        //設定content-disposition響應頭控制瀏覽器以下載的形式開啟檔案,中文檔名要使用URLEncoder.encode方法進行編碼
        response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
        FileReader in = new FileReader(realPath);
        int len = 0;
        char[] buffer = new char[1024];
        PrintWriter out = response.getWriter();
        while ((len = in.read(buffer)) > 0) {
            out.write(buffer,0,len);//將緩衝區的資料輸出到客戶端瀏覽器
        }
        in.close();
    }
    
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

執行結果如下:

正常彈出下載框,此時我們點選【儲存】按鈕將檔案下載下來,如下所示:

可以看到,只下載了5.25MB,而這張圖片的原始大小卻是

  這說明在下載的時候資料丟失了,所以下載不完全,所以這張圖片雖然能夠正常下載下來,但是卻是無法開啟的,因為丟失掉了部分資料,如下所示:

     所以使用PrintWriter流處理位元組資料,會導致資料丟失,這一點千萬要注意,因此在編寫下載檔案功能時,要使用OutputStream流,避免使用PrintWriter流,因為OutputStream流是位元組流,可以處理任意型別的資料,而PrintWriter流是字元流,只能處理字元資料,如果用字元流處理位元組資料,會導致資料丟失。