new InputStream().available()方法的讲解
======================================================
案例一:
先看看下面这段代码(有删节)
public static String send(String sendurl, String sendText) throws Exception {
URL url = null;
URLConnection uc = null;
//建立连接、输出数据等;
String strReceive = "";
try {
InputStream is = uc.getInputStream();
DataInputStream in = new DataInputStream(is);
int blockLen = in.available();
byte block[] = new byte[blockLen];
for (int readLen = -9999; readLen != -1;) {
readLen = in.read(block);
if (readLen != -1)
strReceive = strReceive + new String(block);
}
is.close();
in.close();
} catch (IOException e) {
logger.info("httpl接收错误1:" + e.getMessage());
}
return strReceive;
}
注意红色字体那几行。blockLen被用来创建一个字节数组block,block作为数据缓冲来读取inputstream里的数据。然后循环从inputstream中读取数据,写入block中。
考虑一种情况。如果网络阻塞了,inputstream已经打开,但是数据却还没有传输过来,会发生什么?
inputstream.available()方法返回的值是该inputstream在不被阻塞的情况下一次可以读取到的数据长度。如果数据还没有传输过来,那么这个inputstream势必会被阻塞,从而导致inputstream.available返回0。而对inputstream.read(byte[] b)而言,如果b的长度等于0,该方法将返回0。
回头看看这个循环体的结束条件,是readLen == -1时跳出。显然,上面提到的网络阻塞情况发生之后,代码将陷入这个死循环当中。
这是我们在工程应用中遇到的一个问题。由外包商提供的工具jar包中的这段代码,直接将我们的服务器拉进了死循环。
我们的解决方法,是将整个接收与发送的方法进行改写,使用了下面的这段代码:
HttpClient client = new HttpClient();
PostMethod method = new PostMethod(prpUrl);
method.setRequestBody(sendstr);
method.getParams().setParameter(
HttpMethodParams.HTTP_CONTENT_CHARSET, "GBK");
client.executeMethod(method);
// rtnXml = method.getResponseBodyAsString();
InputStream txtis = method.getResponseBodyAsStream();
BufferedReader br = new BufferedReader(new InputStreamReader(txtis));
String tempbf;
StringBuffer html = new StringBuffer(100);
while ((tempbf = br.readLine()) != null) {
html.append(tempbf);
}
rtnXml = html.toString();
method.releaseConnection();
确确实实的,解决了问题。
如果仍然要采用原方法中手动打开输入流、建立缓冲区、循环读取数据的方法,那么不应该用available这个字段来作为缓冲区的初始长度。可以考虑手工设定一个固定值;或者读取http报文头的content-length属性值。最后的这种方式没有尝试过。
=====================================================================================
案例二
如果用inputStream对象的available()方法获取流中可读取的数据大小,通常我们调用这个函数是在下载文件或者对文件进行其他处理时获取文件的总大小。
以前在我们初学File和inputStream和outputStream时,有需要将文件从一个文件夹复制到另一个文件夹中,这时候我们用的就是inputStream.available()来获取文件的总大小,而且屡试不爽。
但是当我们要从网络URL中下载一个文件时,我们发现得到的数值并不是需要下载的文件的总大小。
好吧。我们看看JDK文档中怎么解释。
available
public int available()throws IOException
-
返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。下一个调用可能是同一个线程,也可能是另一个线程。一次读取或跳过此估计数个字节不会受阻塞,但读取或跳过的字节数可能小于该数。
注意,有些
InputStream
的实现将返回流中的字节总数,但也有很多实现不会这样做。试图使用此方法的返回值分配缓冲区,以保存此流所有数据的做法是不正确的。如果已经调用
close()
方法关闭了此输入流,那么此方法的子类实现可以选择抛出IOException
。类
InputStream
的available
方法总是返回0
。此方法应该由子类重写。
-
- 返回:
-
可以不受阻塞地从此输入流读取(或跳过)的估计字节数;如果到达输入流末尾,则返回
0
。 - 抛出:
-
IOException
- 如果发生 I/O 错误。
inputStream 源代码
- /**
- *Returnsthenumberofbytesthatareavailablebeforethisstreamwill
- *block.Thisimplementationalwaysreturns0.Subclassesshouldoverride
- *andindicatethecorrectnumberofbytesavailable.
- *
- *@returnthenumberofbytesavailablebeforeblocking.
- *@throwsIOException
- *ifanerroroccursinthisstream.
- *@sinceAndroid1.0
- */
- <SPANstyle="COLOR:#ff0000">publicintavailable()throwsIOException{
- return0;
- }</SPAN>
/*** Returns the number of bytes that are available before this stream will* block. This implementation always returns 0. Subclasses should override* and indicate the correct number of bytes available.* * @return the number of bytes available before blocking.* @throws IOException* if an error occurs in this stream.* @since Android 1.0*/ public int available() throws IOException {return 0;}
这里返回的是 0 值。
所以说要从网络中下载文件时,我们知道网络是不稳定的,也就是说网络下载时,read()方法是阻塞的,说明这时我们用
inputStream.available()获取不到文件的总大小。
但是从本地拷贝文件时,我们用的是FileInputStream.available(),难道它是将先将硬盘中的数据先全部读入流中?
然后才根据此方法得到文件的总大小?
好吧,我们来看看FileInputStream源代码吧
- /**
- *Returnsthenumberofbytesthatareavailablebeforethisstreamwill
- *block.Thismethodalwaysreturnsthesizeofthefileminusthecurrent
- *position.
- *
- *@returnthenumberofbytesavailablebeforeblocking.
- *@throwsIOException
- *ifanerroroccursinthisstream.
- *@sinceAndroid1.0
- */
- @Override
- publicintavailable()throwsIOException{
- openCheck();
- //BEGINandroid-added
- //Androidalwaysusestheioctl()methodofdeterminingbytes
- //available.Seethelongdiscussionin
- //org_apache_harmony_luni_platform_OSFileSystem.cppaboutits
- //use.
- <SPANstyle="COLOR:#ff0000">returnfileSystem.ioctlAvailable(fd.descriptor);</SPAN>
- //ENDandroid-added
- //BEGINandroid-deleted
- //synchronized(repositioningLock){
- stdinrequiresspecialhandling
- //if(fd==FileDescriptor.in){
- //return(int)fileSystem.ttyAvailable();
- //}
- //
- //longcurrentPosition=fileSystem.seek(fd.descriptor,0L,
- //IFileSystem.SEEK_CUR);
- //longendOfFilePosition=fileSystem.seek(fd.descriptor,0L,
- //IFileSystem.SEEK_END);
- //fileSystem.seek(fd.descriptor,currentPosition,
- //IFileSystem.SEEK_SET);
- //return(int)(endOfFilePosition-currentPosition);
- //}
- //ENDandroid-deleted
- }
/*** Returns the number of bytes that are available before this stream will* block. This method always returns the size of the file minus the current* position.* * @return the number of bytes available before blocking.* @throws IOException* if an error occurs in this stream.* @since Android 1.0*/@Overridepublic int available() throws IOException {openCheck();// BEGIN android-added// Android always uses the ioctl() method of determining bytes// available. See the long discussion in// org_apache_harmony_luni_platform_OSFileSystem.cpp about its// use.return fileSystem.ioctlAvailable(fd.descriptor);// END android-added // BEGIN android-deleted// synchronized (repositioningLock) {// // stdin requires special handling// if (fd == FileDescriptor.in) {// return (int) fileSystem.ttyAvailable();// }//// long currentPosition = fileSystem.seek(fd.descriptor, 0L,// IFileSystem.SEEK_CUR);// long endOfFilePosition = fileSystem.seek(fd.descriptor, 0L,// IFileSystem.SEEK_END);// fileSystem.seek(fd.descriptor, currentPosition,// IFileSystem.SEEK_SET);// return (int) (endOfFilePosition - currentPosition);// }// END android-deleted}
这里重写了inputStream中的available()方法
关键是:fileSystem.ioctlAvailable(fd.descriptor);
调用了FileSystem这是java没有公开的一个类,JavaDoc API没有。
其中
fileSystem 是一个IFileSystem对象,IFileSySTEM是java没有公开的一个类,JavaDoc API中没有;
fd是一个FileDescriptor对象,即文件描述符
说明这句代码应该是通过文件描述符获取文件的总大小,而并不是事先将磁盘上的文件数据全部读入流中,再获取文件总大小
搞清楚了这些,但是我们的主要问题没有解决,怎么获得网络文件的总大小?
我想原理应该都差不多,应该也是通过一个类似文件描述符的东西来获取。
网络下载获取文件总大小的代码:
- <SPANstyle="COLOR:#ff0000">HttpURLConnectionhttpconn=(HttpURLConnection)url.openConnection();
- httpconn.getContentLength();</SPAN>
HttpURLConnection httpconn = (HttpURLConnection)url.openConnection();httpconn.getContentLength();
我们再来看看httpconn.getContentLength();
- /**
- *Getsthecontentlengthinbytesspecifiedbytheresponseheaderfield
- *{@codecontent-length}or{@code-1}ifthisfieldisnotset.
- *
- *@returnthevalueoftheresponseheaderfield{@codecontent-length}.
- *@sinceAndroid1.0
- */
- publicintgetContentLength(){
- <SPANstyle="COLOR:#ff0000">returngetHeaderFieldInt("Content-Length",-1);</SPAN>//$NON-NLS-1$
- }
/*** Gets the content length in bytes specified by the response header field* {@code content-length} or {@code -1} if this field is not set.* * @return the value of the response header field {@code content-length}.* @since Android 1.0*/public int getContentLength() {return getHeaderFieldInt("Content-Length", -1); //$NON-NLS-1$}
关键:getHeaderFieldInt("Content-Length", -1);
意思是从http预解析头中获取“Content-length”字段的值
其实也是类似从文件描述符中获取文件的总大小
===========================================================================
我的简练的总结:
FileInputStream 的 available()就是返回的的,实际可读字节数,也就是总大小
FileInputStream的available() 方法返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。
在某些情况下,非阻塞的读取(或跳过)操作在执行很慢时看起来受阻塞,例如,在网速缓慢的网络上读取大文件时。
new InputStream().available()方法的讲解相关推荐
- python中的object是什么意思_Python object类中的特殊方法代码讲解
python版本:3.8class object: """ The most base type """ # del obj.xxx或del ...
- think php a方法,PHP_ThinkPHP之A方法实例讲解,ThinkPHP的A方法用于在内部实例 - phpStudy...
ThinkPHP之A方法实例讲解 ThinkPHP的A方法用于在内部实例化控制器,其调用格式为: A('[项目://][分组/]模块','控制器层名称') 最简单的用法: $User = A('Use ...
- mysql json 引号 双引号_关于JSON字符串key缺少双引号的解决方法 的讲解
JSON字符串key缺少引号的解决方法 JSON字符串是key:value形式的字符串,正常key是由双引号括起来的. 例如:<?php $data = array('name'=>'fd ...
- 计算机模拟培训,材料的计算机模拟方法培训讲解.ppt
材料的计算机模拟方法培训讲解.ppt 研究背景: 感度 分子结构 晶体结构 晶格聚集态 缺陷 空穴 起爆机理 起爆过程 效率设计 HMX 研究背景: Bowden [1]和Kanllet [2]等人很 ...
- php解密方法,六种php加密解密方法实例讲解
代码演示如下: 方法一 function encryptDecrypt($key, $string, $decrypt){ if($decrypt){ $decrypted = rtrim(mcryp ...
- 了解Oracle补丁以及应用方法(案例讲解)
http://www.luocs.com/archives/737.html 了解Oracle补丁以及应用方法(案例讲解) By luocs ( 三月 18, 2013 at 上午 11:01) · ...
- Require使用方法详细讲解
Require使用方法详细讲解 文章目录 Require使用方法详细讲解 一.AMD 规范 1,AMD 基本介绍 2,AMD 模块规范 二.RequireJS 介绍 1,什么是 RequireJS 2 ...
- es6 三点运算符_基于es6三点运算符的使用方法(实例讲解)
先看一个es6规范下三点运算符的使用实例: let fun=function(a,...list){ console.log(a,list); }; fun('0','a','b','c');//0 ...
- 具体数学 - 成套方法例题讲解
具体数学 - 成套方法例题讲解 大家好,我是W 我自认为的数学不是很好,所以需要反复琢磨才能弄懂别人很容易就能掌握的知识,我也希望跟我一样的人能够得到好的辅导,所以我决定新开一个标签把具体数学中一些问 ...
- [Android Pro] InputStream.skip方法的思考
参考 : http://blog.csdn.net/gsyzhu/article/details/8102286 在java.io.InputStream类中定义了skip这个方法.在API中的描述如 ...
最新文章
- 我挖掘Kafka底层原理!发现了它火爆宇宙的3个真相!
- linux目录加了一把锁怎么办,linux 文件加锁 lockf 小结
- rabbit-mq cluster安装
- LeetCode312.BurstBalloons
- 今年2月的微盟“删库”主角被判 6 年有期徒刑
- Facebook向量召回双塔模型
- python 输入数字变成密码_如何在python中检查数字的“密码”
- c语言课程设计小时钟,【图片】发几个C语言课程设计源代码(恭喜自己当上技术小吧主)【东华理工大学吧】_百度贴吧...
- CSU2080: 航行日志的修复
- Java五子棋(人机版),昨天买的棋子今天就用不上了
- 百度地图标注点+搜索
- 2-6年“iOS开发”,跳槽简历思路(必备)
- 算术编码原理及其python实现
- java通过schema校验xml
- 子网掩码和prefixlength
- 河南城建学院matlab报告,河南城建学院MATLAB上机实验答案
- Flappybird的变现途径
- 如何重建深层神经网络的可塑性?
- vue项目中使用阿里巴巴矢量图库图标的操作步骤
- 新購電腦筆記 - G1.Sniper B7 內建網路晶片在 Mint 17.2(Cinnamon)上無法使用(已解決)...