遇到的问题
笔者在进行开发时,从网络中获取对应文件时,需要知道inputStream的大小,经过搜索引擎查询之后得知available可以解决该问题。结果在项目运行时候经常出现数据为0。
原因
在解释这个问题之前,我们都必须知道available返回值,根据api的提示是avaliable用于返回非阻塞情况下,一次性可读的字节数。
而SocketInputStream是阻塞的,available的数字每次取决于网络状况。
我们都知道网络是不可靠的,这就导致了有时候获取的是0(双方tcp建立通信,发送方才发送了数据接收方还没来得及接受就被读取了),有时候获取的又不是0,极有可能是因为网络波动导致的SocketInputStream阻塞。
相关知识
为什么FileInputStream.available()读取大小没有问题?
从源码我们可以看出文件流重写了该方法,且获取文件是通过fd.descriptor
即文件描述符来获取文件大小的
**
* 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
*
@Override
public 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.
<span style="color: #ff0000;">return fileSystem.ioctlAvailable(fd.descriptor);<span>
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
}
为什么网络传输的小文件传输时就没有问题呢?
以太网最大mtu为1500,这意味着网络传输中,在保证网络质量的情况下,1500以内的网络文件大小是可以通过available获取的。若大于1500则文件会被切片,导致文件流在available时获取的大小很不稳定。
读者可以根据下方代码下载小于1500字节的数据试试看
public static void getHttpRequestData(String urlPath) {
// 首先抓取异常并处理
String returnString = "1";
try {
// 代码实现以GET请求方式为主,POST跳过
/** 1 GET方式请求数据 start*/
// 1 创建URL对象,接收用户传递访问地址对象链接
URL url = new URL(urlPath);
// 2 打开用户传递URL参数地址
HttpURLConnection connect = (HttpURLConnection) url.openConnection();
// 3 设置HTTP请求的一些参数信息
connect.setRequestMethod("GET"); // 参数必须大写
connect.connect();
// 4 获取URL请求到的数据,并创建数据流接收
InputStream isString = connect.getInputStream();
System.out.println(isString.available());
} catch (Exception e) {
e.printStackTrace();
}
}
对应解决方案
既然文件流可以根据文件描述,那么为什么网络文件不能根据网络传输协议中的描述符获取文件大小呢?
这时候,我们就想到http协议中一个重要的字段header中的Content-Length。在http的协议中Content-Length头部告诉浏览器报文中实体数据的大小。
URL url = new URL(urlPath);
// 2 打开用户传递URL参数地址
HttpURLConnection connect = (HttpURLConnection) url.openConnection();
// 3 设置HTTP请求的一些参数信息
connect.setRequestMethod("GET"); // 参数必须大写
connect.connect();
// 4 获取URL请求到的数据,并创建数据流接收
InputStream isString = connect.getInputStream();
System.out.println(connect.getContentLength());