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()方法,打印的結果回覆了正常。