關於 InputStream.available()

InputStream.available()  可以獲取InputStream的總的長度嗎

答案是不確定。總結如下:

處理文件輸入流: 可以

處理網絡流(socket):不可以

官方的說明是:

 

 

總之,儘量還是別使用它來獲取流的長度, 如果是文件, 我們完全可以使用 file對象的.length()  方法啊! 其他流 也應該有對應的方式獲取流的長度,實在沒有,那怎麼辦? 只能讀流完之後再獲取。

 

 

有人說:

InputStream的available()方法返回此輸入流下一個方法調用可以不受阻塞地從此輸入流讀取(或跳過)的估計字節數。下一個調用可能是同一個線程,也可能是另一個線程。一次讀取或跳過此估計數個字節不會受阻塞,但讀取或跳過的字節數可能小於該數。
注意,有些 InputStream 的實現將返回流中的字節總數,但也有很多實現不會這樣做。試圖使用此方法的返回值分配緩衝區,以保存此流所有數據的做法是不正確的。
JDK的解釋意味着,我們不能通過該方法來獲取InputStream的字節大小進而據此大小來初始化緩衝區的大小。
那麼我們只能while循環read:

OutputStream out = new BufferedOutputStream(response.getOutputStream());
        byte[] bytes = new byte[1024];
        int len;
        while ((len = in.read(bytes)) != -1) {
            out.write(bytes, 0, len);
        }
        in.close();
        out.flush();
        out.close();

有人說:

所以調用網絡流(socket)的available()方法前,一定記得要先調用read()方法,這樣才能避免獲取爲0的不正確情況。

int firstByte = inputStream.read();
int length = inputStream.available();

沒試過,感覺是行不通的,因爲 網絡緩存的原因, 並不是說你調用一次inputStream.read(); 就能夠把 inputStream所有的數據加載到tcp緩存區。

 

---- 下面是轉載 https://www.cnblogs.com/zjfjava/p/10829241.html

在處理文件輸入流時,可以通過調用available()方法來獲取還有多少字節可以讀取,根據該數值創建固定大小的byte數組,從而讀取輸入流的信息。

 

但是在處理網絡流(socket)時,通過available()方法對輸入流進行長度判斷,數值爲0,這意味着對方發送的流中無數據,但實際上並非如此。

原因在於:

網絡通訊往往是間斷性的,一串字節往往分幾批進行發送。例如對方發來字節長度100的數據,本地程序調用available()方法有時得到0,有時得到50,有時能得到100,大多數情況下是0。這可能是對方還沒有響應,也可能是對方已經響應了,但是數據還沒有送達本地。也許分3批到達,也許分兩批,也許一次性到達。

詳細解釋參考OSI網絡7層結構:

我們進行的數據接收只是基於應用層,網絡傳輸的最上層,數據從一端到另一端傳輸的時候,會在傳輸層分解成適合的數據包。傳輸層(Transport Layer)是OSI 模型中最重要的一層。傳輸協議同時進行流量控制或是基於接收方可接收數據的快慢程度規定適當的發送速率。除此之外,傳輸層按照網絡能處理的最大尺寸將較長的數據包進行強制分割。例如,以太網無法接收大於1500 字節的數據包。發送方節點的傳輸層將數據分割成較小的數據片,同時對每一數據片安排一序列號,以便數據到達接收方節點的傳輸層時,能以正確的順序重組。該過程即被稱爲排序。工作在傳輸層的一種服務是 TCP/IP 協議套中的TCP(傳輸控制協議),另一項傳輸層服務是 IPX/SPX 協議集的 SPX(序列包交換)。

InputStream的available()方法的作用是返回此輸入流在不受阻塞情況下能讀取的字節數。網絡流與文件流不同的關鍵就在於是否“受阻”二字,網絡socket流在讀取時如果沒有內容read()方法是會受阻的,所以從socket初始化的輸入流的available也是爲零的,所以要read一字節後再使用,這樣可用的字節數就等於 available + 1。但文件讀取時read()一般是不會受阻的,因爲文件流的可用字節數 available = file.length(),而文件的內容長度在創建File對象時就已知了。 

所以調用網絡流(socket)的available()方法前,一定記得要先調用read()方法,這樣才能避免獲取爲0的不正確情況。

複製代碼
   //將接收到的數據存到字節數組bytes
    int firstByte = inputStream.read();
    int length = inputStream.available();
    byte[] bytes = new byte[length+1];
    bytes[0] = (byte)firstByte;
    inputStream.read(bytes,1,length);
複製代碼

另外,在文件上載和表單提交的過程中,可以使用 request.getContentLength()方法代替InputStream.available()方法,通過調用 request.getContentLength() 得到 Content-Length ,並定義一個與 Content-Length 大小相等的字節數組 buffer,從HttpServletRequest 的實例 request 中得到一個 InputStream, 並把它讀入 buffer 中。然後使用 FileOutputStream 將 buffer 寫入指定文件。

複製代碼
// ReceiveServlet.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
//示例程序:記錄下Form提交上來的數據,並存儲到Log文件中
public class  ReceiveServlet extends HttpServlet
{
    public void doPost(HttpServletRequest request,HttpServletResponse response)
    throws IOException, ServletException
    {
//1
        int len = request.getContentLength();
        byte buffer[] = new byte[len];
//2
        InputStream in = request.getInputStream();
        int total = 0;
        int once = 0;
        while ((total < len) && (once >=0)) {
            once = in.read(buffer,total,len);
            total += once;
        }
//3
        OutputStream out=new BufferedOutputStream(
            new FileOutputStream("Receive.log",true));
        byte[] breaker="\r\nNewLog: -------------------->\r\n".getBytes();
        System.out.println(request.getContentType());
        out.write(breaker,0,breaker.length);
        out.write(buffer);
        out.close();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章