Request.getInputStrema只能讀取一次的分析過程

1. 咱們先來看一下繼承關係HttpServletRequest 接口繼承ServletRequest接口java

public abstract interface  ServletRequest
{數組

public abstract ServletInputStream getInputStream()  throws IOException;less

this

從上面可知request.getInputStream()返回的是ServletInputSteam。查看ServletInputStream源碼可知,ServletInputStream extends InputSteamspa

2.爲何說request.getInputStream()只能讀取一次呢,再讀一次就沒有內容了,這個問題要複習下java的IO知識了。指針

,在java中讀取一個文件或者字符串的內容的代碼你們都會寫,下邊是使用ByteArrayInputStream和ByteArrayOutputStream進行演示:code

@Test
  public void testByteArrayInputStream() throws Exception {
         String str = "AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
          //ByteArrayInputStream是把一個byte數組轉換成一個字節流,讀取的內容是從byte數組中讀取的
         ByteArrayInputStream byteInputStream = new ByteArrayInputStream(str.getBytes());
          
          //ByteArrayOutputStream生成對象的時候,是生成一個100大小的byte的緩衝區,寫入的時候,是把內容寫入內存中的一個緩衝區
          ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(100);
        
         int i =0;
         byte [] b = new byte[100];
         while((i = byteInputStream.read(b))!= -1){
             byteOutput.write(b, 0, i);
         }
         System.out.println(new String(byteOutput.toByteArray()));
         
     }

打印結果是:AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBorm

把一個String字符串的內容使用ByteArrayOutputStream讀取出來,而後打印顯示。這個代碼沒有什麼問題,估計你們都能寫出來,可是看一下下邊添加一行代碼以後的內容:對象

 

@Test
  public void testByteArrayInputStream() throws Exception {
         String str = "AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
          //ByteArrayInputStream是把一個byte數組轉換成一個字節流,讀取的內容是從byte數組中讀取的
         ByteArrayInputStream byteInputStream = new ByteArrayInputStream(str.getBytes());

          //調用這個方法,會影響到下次讀取,下次再調用這個方法,讀取的起始點會後移5個byte
          byteInputStream.read(new byte[5]);
          
          //ByteArrayOutputStream生成對象的時候,是生成一個100大小的byte的緩衝區,寫入的時候,是把內容寫入內存中的一個緩衝區
          ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(100);
        
         int i =0;
         byte [] b = new byte[100];
         while((i = byteInputStream.read(b))!= -1){
             byteOutput.write(b, 0, i);
         }
         System.out.println(new String(byteOutput.toByteArray()));
         
     }

打印結果是:CCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBblog

我在第8行添加了一行代碼,這行代碼能夠把String生成的byte數組中讀取5個字節的內容,這行代碼會影響到後邊第15行byteInputStream的讀取結果,顯然」AAAAA「在輸出中沒有了,這是爲何呢?我感受須要查看java中ByteArrayInputStream的Read方法的實現源碼,查看的結果其實能夠總結一個句話,就是在InputStream讀取的時候,會有一個pos指針,他指示每次讀取以後下一次要讀取的起始位置,在API文檔中是這樣解釋的:(看過InputStream源碼的都明白,read方法其實調用的都是帶有三個參數的方法)

public int read(byte[] b,int off, int len)   
 Reads up to len bytes of data into an array of bytes from this input stream. If pos equals count, then -1 is returned to indicate end of file. Otherwise, the number k of
bytes read is equal to the smaller of len and count-pos. If k is positive, then bytes buf[pos] through buf[pos+k-1] are copied into b[off] through b[off+k-1] in the manner
performed by System.arraycopy. The value k is added into pos and k is returned.

 

   就是在每次讀取的時候會更新pos的值,當你下次再來讀取的時候是從pos的位置開始的,而不是從頭開始,因此第二次獲取String中的值的時候是不全的,」AAAAA「丟掉了,這也就致使了兩次調用request.getInputStream,第二次的時候確定獲取不了值,由於第一次讀取完成以後pos指針在末尾,下次再讀取確定讀取不到,同request.getInputStream兩次調用返回的對象是同一個對象。讀取的是同一個Stream。

可是仔細查看API文檔你會發現有這樣一個方法:

public void reset()
Resets the buffer to the marked position. The marked position is 0 unless another position was marked or an offset was specified in the constructor. 

就是能夠把pos的指針的位置重置爲起始位置,可是調用它是有條件的,不是全部的IO讀取流都能調用這個方法.看一下有這個方法

public boolean markSupported()
Tests if this input stream supports the mark and reset methods. Whether or not mark and reset are supported is an invariant property of a particular input stream instance. 
The markSupported method of InputStream returns false.

這個方法能夠判斷是否是支持reset()方法的調用,

經過查看ServletInputStream的源碼,

ServletInputStream繼承了InputStream同時沒有重寫reset()方法,查看一下InputStream源碼,InputStream的reset()方法源碼是這樣的:

public synchronized void reset() throws IOException {
         throw new IOException("mark/reset not supported");
     }

調用reset方法直接拋出異常,因此ServletInputStream是不能調用reset方法,這就致使了只能調用一次getInputStream(),第二次調用的時候沒有辦法獲取到InputStream流中的緣由。

如今咱們更改一下上邊讀取String字符串的例子:

 1       @Test
 2     public void testByteArrayInputStream() throws Exception {
 3         String str = "AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
 4         //ByteArrayInputStream是把一個byte數組轉換成一個字節流,讀取的內容是從byte數組中讀取的
 5         ByteArrayInputStream byteInputStream = new ByteArrayInputStream(str.getBytes());
 6         
 7         //調用這個方法,會影響到下次讀取,下次再調用這個方法,讀取的起始點會後移5個byte
 8         byteInputStream.read(new byte[5]);
 9         byteInputStream.reset();//調用reset方法可使read中的pos指針復位
10         
11         
12         //ByteArrayOutputStream生成對象的時候,是生成一個100大小的byte的緩衝區,寫入的時候,是把內容寫入內存中的一個緩衝區
13         ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(100);
14         
15         int i =0;
16         byte [] b = new byte[100];
17         while((i = byteInputStream.read(b))!= -1){
18             byteOutput.write(b, 0, i);
19         }
20         System.out.println(new String(byteOutput.toByteArray()));
21     }

打印結果是:AAAAACCCCcCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

在第8行添加一行代碼,byteInputStream調用reset()方法,打印的結果回覆了正常。

相關文章
相關標籤/搜索