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

response、request對象

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

既然request對象表明http請求,那麼咱們獲取瀏覽器提交過來的數據,找request對象便可。response對象表明http響應,那麼咱們向瀏覽器輸出數據,找response對象便可。php

什麼是HttpServletResponse對象?

http響應由狀態行、實體內容、消息頭、一個空行組成。HttpServletResponse對象就封裝了http響應的信息。html

HttpServletResponse的應用

調用getOutputStream()方法向瀏覽器輸出數據

  • 調用getOutputStream()方法向瀏覽器輸出數據,getOutputStream()方法可使用print()也可使用write(),它們有什麼區別呢?咱們試驗一下。代碼以下
//獲取到OutputStream流
        ServletOutputStream servletOutputStream = response.getOutputStream();

        //向瀏覽器輸出數據
        servletOutputStream.print("aaaa");
  • 成功輸出,好像沒什麼毛病。

  • 咱們試着輸出中文試試
//獲取到OutputStream流
        ServletOutputStream servletOutputStream = response.getOutputStream();

        //向瀏覽器輸出數據
        servletOutputStream.print("中國!");
  • 出異常了!!!

  • 爲何會出現異常呢?在io中咱們學過,outputStream是輸出二進制數據的print()方法接收了一個字符串print()方法要把「中國」改爲二進制數據,Tomcat使用IOS 8859-1編碼對其進行轉換,「中國」根本對ISO 8859-1編碼不支持。因此出現了異常
  • 咱們再看看write()方法,先向瀏覽器輸出英文數據
response.getOutputStream().write("aaa".getBytes());
  • 沒有問題

  • 再試試輸出中文數據
response.getOutputStream().write("你好呀我是中國".getBytes());
  • 貌似也沒有問題。

  • 爲何使用write()方法可以正常向瀏覽器輸出中文呢?"你好呀我是中國".getBytes()這句代碼在轉成byte[]數組的時候默認查的是gb2312編碼,而「你好呀我是中國」支持gb2312編碼,因此能夠正常顯示出來。
  • 可是,程序要實現通用性,應該使用的是UTF-8編碼,咱們在字符串轉換成字節數組時指定UTF-8編碼,看看會怎麼樣。
response.getOutputStream().write("你好呀我是中國".getBytes("UTF-8"));
  • 好的,成功把它搞成亂碼了!!!

  • 爲何它變成了亂碼呢?緣由是這樣的:我在向服務器輸出的中文是UTF-8編碼的,而瀏覽器採用的是GBK,GBK想顯示UTF-8的中文數據,不亂碼纔怪呢


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

  • 亂碼問題又解決了。但是,每次編寫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,亂碼問題也解決了

  • 另外,除了使用HttpServletResponse對象設置消息頭的方法,我能夠使用html的標籤模擬一個http消息頭web

  • 下面是代碼:數組

//獲取到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"));
  • 亂碼問題也能夠解決

調用getWriter()方法向瀏覽器輸出數據

  • 對於getWriter()方法而言,是Writer的子類,那麼只能向瀏覽器輸出字符數據,不能輸出二進制數據
  • 使用getWriter()方法輸出中文數據,代碼以下:
//獲取到printWriter對象
        PrintWriter printWriter = response.getWriter();
        printWriter.write("看完博客點贊!");
  • 喜聞可見的事又出現了,我又出現亂碼了。

  • 爲何出現亂碼了呢?因爲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("看完博客點贊!"); 
  • 我再訪問了一下,個人天!看起來更亂了!

  • 爲何亂碼問題尚未解決?細心的朋友會發現,我只是在中文轉換的時候把碼錶設置成UTF-8,可是瀏覽器未必是使用UTF-8碼錶來顯示數據的呀
  • 好的,咱們來看看瀏覽器的編碼格式,果真,瀏覽器使用GB2312顯示UTF-8的數據,不亂碼纔怪呢

  • 這個問題咱們在上面已是有兩種方法解決了【使用標籤模擬消息頭、設置消息頭】,Servlet還提供了一個方法給咱們
//設置瀏覽器用UTF-8編碼顯示數據
        response.setContentType("text/html;charset=UTF-8");
  • 好的,咱們再來訪問一下

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

        //獲取到printWriter對象
        PrintWriter printWriter = response.getWriter();
        printWriter.write("看完博客點贊!");


實現文件下載

下載資源咱們在平常中也很經常使用,它是怎麼作到的呢?要可以給別人下載,服務器就應該有這個資源瀏覽器

  • 如今我web站點下有一個圖片了!

  • 既然瀏覽器發送全部的請求都是去找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();
  • 當我訪問時,瀏覽器就提示下載了。
    markdown

  • 也能夠成功打開!

  • 如今問題又有了,若是我文件的名字是中文呢?

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

  • 爲了解決文件名亂碼,咱們要進行URL編碼,代碼以下:
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
  • 再次訪問時,文件名亂碼問題就解決了!


實現自動刷新

以規定的時間讓頁面刷新,更新資源

  • 讓瀏覽器實現自動刷新,那確定又是修改消息頭了。
//每3秒自動刷新網頁一次
        response.setHeader("Refresh", "3");
  • 爲了更好的看效果,咱們加入時間值進去
response.getWriter().write("time is :" + System.currentTimeMillis());
  • 每三秒時間值就會發生變化

  • 學完上面的,好像沒有什麼用,本身上網的時候誰看得見這樣的東西。自動刷新,可以實現頁面的跳轉
  • 咱們登錄完網站,不少時候都會看見【登錄成功,3秒後自動跳轉….】,其實這個就是用Refresh來完成的。
response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("3秒後跳轉頁面.....");

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


設置緩存

瀏覽器自己就存在着緩存機制

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

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

  • 像股票類型的網頁是不能取緩存的數據的,數據都是要不斷更新的。下面我就禁止緩存的功能
//瀏覽器有三消息頭設置緩存,爲了兼容性!將三個消息頭都設置了
        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

  • 壓縮的原理是什麼?咱們知道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);
  • 咱們來對比一下壓縮前的大小和壓縮後的大小

  • 數據的確是壓縮了,然而,爲何又亂碼了啊?很簡單,既然你壓縮了數據,你寫給瀏覽器,瀏覽器是不知道你這是壓縮後的數據,它是以正常的方式打開數據的。這固然形成亂碼啦!,如今我要告訴瀏覽器我這是壓縮數據
//告訴瀏覽器這是gzip壓縮的數據
        response.setHeader("Content-Encoding","gzip");

        //再將壓縮的數據寫給瀏覽器
        response.getOutputStream().write(bytes);
  • 再次訪問一下


生出隨機圖片

生成隨機圖片這是很是常見的。在咱們登錄的時候常常要寫驗證碼,而那些驗證碼是一張圖片,就是經過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());
  • 咱們來訪問一下,看下圖片長什麼樣

  • 這樣太醜了,咱們把背景改爲白色看看
//把白色填充整張圖片
        graphics.setColor(Color.white);
        graphics.fillRect(0, 0, 80, 20);
  • 再看看效果,這明顯好看多了

  • 好的,咱們的圖片數字不多是人工寫的,數字應該是隨機產生的!這個就簡單了。如今我要生成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

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

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

  • 從圖上看,咱們看到了兩個狀態碼,一個是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方法關閉該輸出流對象。
相關文章
相關標籤/搜索