Servlet第三篇【request和response簡介、response的常見應用】

Java3y發表於2018-02-04

response、request物件

Tomcat收到客戶端的http請求,會針對每一次請求,分別建立一個代表請求的request物件、和代表響應的response物件

既然request物件代表http請求,那麼我們獲取瀏覽器提交過來的資料,找request物件即可。response物件代表http響應,那麼我們向瀏覽器輸出資料,找response物件即可。

什麼是HttpServletResponse物件?

http響應由狀態行、實體內容、訊息頭、一個空行組成。HttpServletResponse物件就封裝了http響應的資訊。

HttpServletResponse的應用

呼叫getOutputStream()方法向瀏覽器輸出資料

  • 呼叫getOutputStream()方法向瀏覽器輸出資料,getOutputStream()方法可以使用print()也可以使用write(),它們有什麼區別呢?我們試驗一下。程式碼如下

        //獲取到OutputStream流
        ServletOutputStream servletOutputStream = response.getOutputStream();

        //向瀏覽器輸出資料
        servletOutputStream.print("aaaa");
複製程式碼
  • 成功輸出,好像沒什麼毛病。

Servlet第三篇【request和response簡介、response的常見應用】

  • 我們試著輸出中文試試

        //獲取到OutputStream流
        ServletOutputStream servletOutputStream = response.getOutputStream();

        //向瀏覽器輸出資料
        servletOutputStream.print("中國!");
複製程式碼
  • 出異常了!!!

Servlet第三篇【request和response簡介、response的常見應用】

  • 為什麼會出現異常呢?在io中我們學過,outputStream是輸出二進位制資料的print()方法接收了一個字串print()方法要把“中國”改成二進位制資料,Tomcat使用IOS 8859-1編碼對其進行轉換,“中國”根本對ISO 8859-1編碼不支援。所以出現了異常
  • 我們再看看write()方法,先向瀏覽器輸出英文資料

		response.getOutputStream().write("aaa".getBytes());

複製程式碼
  • 沒有問題

Servlet第三篇【request和response簡介、response的常見應用】

  • 再試試輸出中文資料

	 response.getOutputStream().write("你好呀我是中國".getBytes());


複製程式碼
  • 貌似也沒有問題。

Servlet第三篇【request和response簡介、response的常見應用】

  • 為什麼使用write()方法能夠正常向瀏覽器輸出中文呢?"你好呀我是中國".getBytes()這句程式碼在轉成byte[]陣列的時候預設查的是gb2312編碼,而**"你好呀我是中國"支援gb2312編碼**,所以可以正常顯示出來。
  • 但是,程式要實現通用性,應該使用的是UTF-8編碼,我們在字串轉換成位元組陣列時指定UTF-8編碼,看看會怎麼樣。

 	response.getOutputStream().write("你好呀我是中國".getBytes("UTF-8"));

複製程式碼
  • 好的,成功把它搞成亂碼了!!!

Servlet第三篇【request和response簡介、response的常見應用】

  • 為什麼它變成了亂碼呢?原因是這樣的:我在向伺服器輸出的中文是UTF-8編碼的,而瀏覽器採用的是GBK,GBK想顯示UTF-8的中文資料,不亂碼才怪呢

Servlet第三篇【request和response簡介、response的常見應用】

  • 既然如此,我將瀏覽器的編碼改成UTF-8試試。

Servlet第三篇【request和response簡介、response的常見應用】

  • 亂碼問題又解決了。可是,每次編寫UTF-8程式時都要去網頁上改編碼格式嗎?這樣明顯不可能的
  • 既然HTTP響應有對瀏覽器說明回送資料是什麼型別的訊息頭,那麼HttpServletResponse物件就應該有相對應的方法告訴瀏覽器回送的資料編碼格式是什麼
  • 於是乎就去查詢Servlet API,找到了設定訊息頭的方法

        //設定頭資訊,告訴瀏覽器我回送的資料編碼是utf-8的
        response.setHeader("Content-Type", "text/html;charset=UTF-8");
        
        response.getOutputStream().write("你好呀我是中國".getBytes("UTF-8"));



複製程式碼
  • 瀏覽器在顯示資料時,自動把頁面的編碼格式置換成UTF-8,亂碼問題也解決了

Servlet第三篇【request和response簡介、response的常見應用】

  • 另外,除了使用HttpServletResponse物件設定訊息頭的方法,我可以使用html的標籤模擬一個http訊息頭

  • 下面是程式碼:


        //獲取到servletOutputStream物件
        ServletOutputStream servletOutputStream = response.getOutputStream();


        //使用meta標籤模擬http訊息頭,告訴瀏覽器回送資料的編碼和格式
        servletOutputStream.write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'>".getBytes());

        servletOutputStream.write("我是中國".getBytes("UTF-8"));


複製程式碼
  • 亂碼問題也可以解決

Servlet第三篇【request和response簡介、response的常見應用】

呼叫getWriter()方法向瀏覽器輸出資料

  • 對於getWriter()方法而言,是Writer的子類,那麼只能向瀏覽器輸出字元資料,不能輸出二進位制資料
  • 使用getWriter()方法輸出中文資料,程式碼如下:

        //獲取到printWriter物件
        PrintWriter printWriter = response.getWriter();
        printWriter.write("看完部落格點贊!");


複製程式碼
  • 喜聞可見的事又出現了,我又出現亂碼了。

Servlet第三篇【request和response簡介、response的常見應用】

  • 為什麼出現亂碼了呢?由於Tomcat是外國人的寫,Tomcat預設的編碼是ISO 8859-1,當我們輸出中文資料的時候,Tomcat會依據ISO 8859-1碼錶給我們的資料編碼,中文不支援這個碼錶呀,所以出現了亂碼
  • 既然如此,我設定一下編碼不就好了嗎,程式碼如下:
		

        //原本是ISO 8859-1的編碼,我設定成UTF-8
        response.setCharacterEncoding("UTF-8");

        //獲取到printWriter物件
        PrintWriter printWriter = response.getWriter();
        printWriter.write("看完部落格點贊!");



複製程式碼
  • 我再訪問了一下,我的天!看起來更亂了!

Servlet第三篇【request和response簡介、response的常見應用】

  • 為什麼亂碼問題還沒有解決?細心的朋友會發現,我只是在中文轉換的時候把碼錶設定成UTF-8,但是瀏覽器未必是使用UTF-8碼錶來顯示資料的呀
  • 好的,我們來看看瀏覽器的編碼格式,果然,瀏覽器使用GB2312顯示UTF-8的資料,不亂碼才怪呢

Servlet第三篇【request和response簡介、response的常見應用】

  • 這個問題我們在上面已經是有兩種方法解決了【使用標籤模擬訊息頭、設定訊息頭】,Servlet還提供了一個方法給我們

        //設定瀏覽器用UTF-8編碼顯示資料
        response.setContentType("text/html;charset=UTF-8");

複製程式碼
  • 好的,我們再來訪問一下

Servlet第三篇【request和response簡介、response的常見應用】

  • 既然Servlet有那麼多方法解決亂碼問題,是不是有一種是最簡便的呢?沒錯!下面這個方法是最簡便的,它不僅設定瀏覽器用UTF-8顯示資料,內部還把中文轉碼的碼錶設定成UTF-8了,也就是說,response.setContentType("text/html;charset=UTF-8");response.setCharacterEncoding("UTF-8")的事情也幹了!

  • 使用getWriter()顯示中文資料,只需要一個方法就搞掂了!


        //設定瀏覽器用UTF-8編碼顯示資料,
        response.setContentType("text/html;charset=UTF-8");

        //獲取到printWriter物件
        PrintWriter printWriter = response.getWriter();
        printWriter.write("看完部落格點贊!");


複製程式碼

Servlet第三篇【request和response簡介、response的常見應用】


實現檔案下載

下載資源我們在日常中也很常用,它是怎麼做到的呢?要能夠給別人下載,伺服器就應該有這個資源

  • 現在我web站點下有一個圖片了!

Servlet第三篇【request和response簡介、response的常見應用】

  • 既然瀏覽器傳送所有的請求都是去找Servlet的話,那麼我就寫一個Servlet,當別人訪問我這個Servlet的時候,它們就可以下載我這個圖片了

  • java的檔案上傳下載都是通過io流來完成的,既然要下載圖片,首先要能夠讀取到它


        //獲取到資源的路徑
        String path = this.getServletContext().getRealPath("/download/1.png");

        //讀取資源
        FileInputStream fileInputStream = new FileInputStream(path);

        //獲取到檔名,路徑在電腦上儲存是\\形式的。
        String fileName = path.substring(path.lastIndexOf("\\") + 1);

複製程式碼
  • 告訴瀏覽器,我要下載這個檔案

	    //設定訊息頭,告訴瀏覽器,我要下載1.png這個圖片
        response.setHeader("Content-Disposition", "attachment; filename="+fileName);

複製程式碼
  • 將讀取到的內容回送給瀏覽器

        //把讀取到的資源寫給瀏覽器
        int len = 0;
        byte[] bytes = new byte[1024];
        ServletOutputStream servletOutputStream = response.getOutputStream();

        while ((len = fileInputStream.read(bytes)) > 0) {
            servletOutputStream.write(bytes, 0, len);
        }

        //關閉資源
        servletOutputStream.close();
        fileInputStream.close();
複製程式碼
  • 當我訪問時,瀏覽器就提示下載了。

    Servlet第三篇【request和response簡介、response的常見應用】

  • 也可以成功開啟!

Servlet第三篇【request和response簡介、response的常見應用】

  • 現在問題又有了,如果我檔案的名字是中文呢?

Servlet第三篇【request和response簡介、response的常見應用】

  • 我們再訪問一下,發現名字亂碼了(怎麼都是亂碼)

Servlet第三篇【request和response簡介、response的常見應用】

  • 為了解決檔名亂碼,我們要進行URL編碼,程式碼如下:

        response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));

複製程式碼
  • 再次訪問時,檔名亂碼問題就解決了!

Servlet第三篇【request和response簡介、response的常見應用】


實現自動重新整理

以規定的時間讓頁面重新整理,更新資源

  • 讓瀏覽器實現自動重新整理,那肯定又是修改訊息頭了。

        //每3秒自動重新整理網頁一次
        response.setHeader("Refresh", "3");

複製程式碼
  • 為了更好的看效果,我們加入時間值進去

	response.getWriter().write("time is :" + System.currentTimeMillis());
	
複製程式碼
  • 每三秒時間值就會發生變化

Servlet第三篇【request和response簡介、response的常見應用】

  • 學完上面的,好像沒有什麼用,自己上網的時候誰看得見這樣的東西。自動重新整理,能夠實現頁面的跳轉
  • 我們登陸完網站,很多時候都會看見【登陸成功,3秒後自動跳轉....】,其實這個就是用Refresh來完成的。

	  	response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("3秒後跳轉頁面.....");

        //三秒後跳轉到index.jsp頁面去,web應用的對映路徑我設定成/,url沒有寫上應用名
        response.setHeader("Refresh", "3;url='/index.jsp'");


複製程式碼
  • 看下效果

Servlet第三篇【request和response簡介、response的常見應用】

Servlet第三篇【request和response簡介、response的常見應用】


設定快取

瀏覽器本身就存在著快取機制

  • 當我第一次訪問index.jsp時,瀏覽器向伺服器發了兩次請求【一個是網頁的,一個是圖片的】

Servlet第三篇【request和response簡介、response的常見應用】

  • 當我第二次訪問index.jsp的時候,瀏覽器將圖片快取起來了!圖片不是重新載入的,是從快取裡面取出來的

Servlet第三篇【request和response簡介、response的常見應用】

  • 像股票型別的網頁是不能取快取的資料的,資料都是要不斷更新的。下面我就禁止快取的功能

        //瀏覽器有三訊息頭設定快取,為了相容性!將三個訊息頭都設定了
        response.setDateHeader("Expires", -1);
        response.setHeader("Cache-Control","no-cache");
        response.setHeader("Pragma", "no-cache");


		//這裡為了看效果
        PrintWriter printWriter = response.getWriter();
        printWriter.print("你好啊" + new Date().toString());


複製程式碼
  • 當然了,如果頁面有些資料不長期更新,你就將它設定成快取,這樣可以提高伺服器的效能

實現資料壓縮

網頁上的資訊量是很大的,如果不將資料壓縮再回送給瀏覽器,這樣就十分耗費流量

  • 現在我有一篇文章要輸出給瀏覽器
        response.setContentType("text/html;charset=UTF-8");

        String ss = "fsdfhsdfhuisdhfusdhfuids" +
                "fsdfdsfsdfsdfdsfdafdsfhsdjfhsdjkfhkjds" +
                "fdsfjdslkfjsldkfjsdlkfjsdkfsdjkff" +
                "fsjdfjdsklfjdsklfjkldsfjlksdjflksdjflkds" +
                "dsjfklsdjflsdjfkldsfkjsdkfjsldkfjsdlfk" +
                "fdsjlkfjdslkfjsdlkfjlkasjflk";
        response.getWriter().write("原來的長度是:"+ss.getBytes().length+"</br>");

        //輸出給瀏覽器
        response.getWriter().write(ss);


複製程式碼
  • 訪問一下可以看到,原來的長度是201

Servlet第三篇【request和response簡介、response的常見應用】

  • 壓縮的原理是什麼?我們知道getOutputStream()和getWriter()都是直接把資料輸出給瀏覽器的。現在我要做的就是讓資料不直接輸出給瀏覽器,先讓我壓縮了,再輸出給瀏覽器java提供了GZIP壓縮類給我們
  • 就讓我們使用GZIP類來對資料壓縮吧

        //GZIP的構造方法需要一個OutputStream子類物件,究竟哪個物件適合,我們看下write()方法
        GZIPOutputStream gzipOutputStream = new GZIPOutputStream();

        //檢視了下API,write()接收的是byte[]型別的。
        gzipOutputStream.write();

複製程式碼
  • 於是我就在建構函式上傳遞個ByteArrayOutputStream給它


        //既然是byte[]型別,那麼我就給他一個ByteArrayOutputStream
        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(new ByteArrayOutputStream());

複製程式碼
  • 而用GZIPOutputStream寫資料的時候,是把資料寫到ByteArrayOutputStream上的,等會還要把資料取出來,再寫給瀏覽器,於是就不能以匿名內部類的方式給GZIPOutputStream,必須把ByteArrayOutputStream定義出來

        //建立GZIPOutputStream物件,給予它ByteArrayOutputStream
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);

        //GZIP對資料壓縮,GZIP寫入的資料是儲存在byteArrayOutputStream上的
        gzipOutputStream.write(ss.getBytes());

        //gzipOutputStream有緩衝,把緩衝清了,並順便關閉流
        gzipOutputStream.close();


複製程式碼
  • 壓縮後的資料取出來,寫給瀏覽器
  		//將壓縮的資料取出來
        byte[] bytes = byteArrayOutputStream.toByteArray();

        //將壓縮的資料寫給瀏覽器
        response.getOutputStream().write(bytes);

複製程式碼
  • 我們來對比一下壓縮前的大小和壓縮後的大小

Servlet第三篇【request和response簡介、response的常見應用】

  • 資料的確是壓縮了,然而,為什麼又亂碼了啊?很簡單,既然你壓縮了資料,你寫給瀏覽器,瀏覽器是不知道你這是壓縮後的資料,它是以正常的方式開啟資料的。這當然造成亂碼啦!,現在我要告訴瀏覽器我這是壓縮資料

        //告訴瀏覽器這是gzip壓縮的資料
        response.setHeader("Content-Encoding","gzip");

        //再將壓縮的資料寫給瀏覽器
        response.getOutputStream().write(bytes);

複製程式碼
  • 再次訪問一下

Servlet第三篇【request和response簡介、response的常見應用】


生出隨機圖片

生成隨機圖片這是非常常見的。在我們登陸的時候經常要寫驗證碼,而那些驗證碼是一張圖片,就是通過HttpServletResponse寫給瀏覽器的。


  • 要生成一張圖片,java提供了BufferedImage類供我們使用


        //在記憶體中生成一張圖片,寬為80,高為20,型別是RGB
        BufferedImage bufferedImage = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);

        //獲取到這張圖片
        Graphics graphics = bufferedImage.getGraphics();

        //往圖片設定顏色和字型
        graphics.setColor(Color.BLUE);
        graphics.setFont(new Font(null, Font.BOLD, 20));

        //往圖片上寫資料,先寫個12345,橫座標是0,縱座標是20【高度】
        graphics.drawString("12345", 0, 20);


複製程式碼
  • 好的,現在我們在記憶體中建立了一張圖片,並寫上了12345。接著,我們要把圖片寫給瀏覽器了把圖片寫給瀏覽器,java又提供了圖片流【ImageIO】給我們使用

        //要往瀏覽器寫一張圖片,那要告訴瀏覽器回送的型別是一張圖片
        response.setHeader("ContentType", "jpeg");
        
        //java提供了圖片流給我們使用,這是一個工具類
        //把圖片傳進去,型別是jpg,寫給瀏覽器
        ImageIO.write(bufferedImage, "jpg", response.getOutputStream());

複製程式碼
  • 我們來訪問一下,看下圖片長什麼樣

Servlet第三篇【request和response簡介、response的常見應用】

  • 這樣太醜了,我們把背景改成白色看看

        //把白色填充整張圖片
        graphics.setColor(Color.white);
        graphics.fillRect(0, 0, 80, 20);

複製程式碼
  • 再看看效果,這明顯好看多了

Servlet第三篇【request和response簡介、response的常見應用】

  • 好的,我們的圖片數字不**可能是人工寫的,數字應該是隨機產生的!**這個就簡單了。現在我要生成7位的隨機數,生成隨機數的方法如下


    private String makeNum() {

        Random random = new Random();

        //這樣就會生成0-7位的隨機數,現在問題又來了,如果隨機數不夠7位呢?如果不夠7位,我們加到7位就行了
        int anInt = random.nextInt(9999999);

        //將數字轉成是字串
        String num = String.valueOf(anInt);

        //判斷位數有多少個,不夠就加
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < 7 - num.length(); i++) {
            stringBuffer.append("0");
        }

        return stringBuffer.append(num).toString();
        
    }

複製程式碼
  • 如果要生成中文,就找中文對映表即可

重定向跳轉

什麼是重定向跳轉呢?點選一個超連結,通知瀏覽器跳轉到另外的一個頁面就叫重定向跳轉。**是通知瀏覽器去跳轉,這很重要。**頁面之間的跳轉有兩種方式:重定向和轉發,至於什麼時候用重定向,什麼用轉發,我在講完HttpServletRequest物件的時候會詳細說明。

  • 我們來使用以下HttpServletResponse物件的重定向

        //重定向到index.jsp頁面
        response.sendRedirect("/zhongfucheng/index.jsp");

複製程式碼
  • 瀏覽器的位址列訪問Servlet222

Servlet第三篇【request和response簡介、response的常見應用】

  • 跳轉到index.jsp頁面,位址列發生了變化

Servlet第三篇【request和response簡介、response的常見應用】

  • 我們再來看看http協議發生了什麼

Servlet第三篇【request和response簡介、response的常見應用】

  • 從圖上看,我們看到了兩個狀態碼,一個是302。一個是200。302狀態碼在http協議中代表的是臨時重定向。舉個例子:我找紀律委員說:給我一份請假表,我要回家。紀律委員告訴我:我這裡沒有請假表,你去找輔導員吧。再看回我訪問Sevlet222時:我找Servlet222,Servlet222告訴瀏覽器:我沒有你想要的資源,你要的資源在index.jsp頁面中,你自己去找吧。
  • 很容易看出重定向是通過302狀態碼和跳轉地址實現的。於是乎,我們設定http訊息頭就可以實現重定向跳轉

        //設定狀態碼是302
        response.setStatus(302);

        //HttpServletResponse把常用的狀態碼封裝成靜態常量了,所以我們可以使用SC_MOVED_TEMPORARILY代表著302
        response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);

        //跳轉的地址是index.jsp頁面
        response.setHeader("Location", "/zhongfucheng/index.jsp");

複製程式碼
  • 其實sendRedirect()方法就是對setStatus()和setHeader()進行封裝,原理就是setStatus()和setHeader()

getWriter和getOutputStream細節

  1. getWriter()和getOutputStream()兩個方法不能同時呼叫。如果同時呼叫就會出現異常
  2. Servlet程式向ServletOutputStream或PrintWriter物件中寫入的資料將被Servlet引擎從response裡面獲取,Servlet引擎將這些資料當作響應訊息的正文,然後再與響應狀態行和各響應頭組合後輸出到客戶端
  3. Servlet的serice()方法結束後【也就是doPost()或者doGet()結束後】,Servlet引擎將檢查getWriter或getOutputStream方法返回的輸出流物件是否已經呼叫過close方法,如果沒有,Servlet引擎將呼叫close方法關閉該輸出流物件.

如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章的同學,可以關注微信公眾號:Java3y

相關文章