最近写了一个项目,需要将HTTP请求的数据读取到一个byte数组中。我的写法如下:

1
2
3
4
5
6
7
8
9
10
11
//下面这段代码想做到的功能大概是:先获取inputStream中数据内容的大小。然后实例化一个数组。最后将所有的数据内容读取到这个数组之中。
try (InputStream inputStream = httpResponse.getEntity().getContent()) {
int size = inputStream.available();
byte[] dataContent = new byte[size];
inputStream.read(dataContent);
return dataContent;
} catch (Exception e) {
logger.error("download qrcode cause exception", e);
}

代码看起来没什么问题。但是最后测试的时候,总是发现数据和想象中的不一样。搜了好久也没有看到解决方案,于是决定去看Java中InputStream的代码,这个代码其实有两个错误的地方。分别是对available函数和read函数的理解。下面我们看看Java源代码中对着两个函数的解释。

首先看看available()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* Returns an estimate of the number of bytes that can be read (or
* skipped over) from this input stream without blocking by the next
* invocation of a method for this input stream. The next invocation
* might be the same thread or another thread. A single read or skip of this
* many bytes will not block, but may read or skip fewer bytes.
*
* <p> Note that while some implementations of {@code InputStream} will return
* the total number of bytes in the stream, many will not. It is
* never correct to use the return value of this method to allocate
* a buffer intended to hold all data in this stream.
*
* <p> A subclass' implementation of this method may choose to throw an
* {@link IOException} if this input stream has been closed by
* invoking the {@link #close()} method.
*
* <p> The {@code available} method for class {@code InputStream} always
* returns {@code 0}.
*
* <p> This method should be overridden by subclasses.
*
* @return an estimate of the number of bytes that can be read (or skipped
* over) from this input stream without blocking or {@code 0} when
* it reaches the end of the input stream.
* @exception IOException if an I/O error occurs.
*/
public int available() throws IOException {
return 0; //这里可以看到available函数默认值返回的是0,可能具体的实现方式返回的数值不同,但是可以肯定的一点是。这里返回的不是HTTP请求所获取的总数据的长度。
}

上面的注释的意思大概是,这个函数返回一个从inputStream中获取数据的估计值(注意不是精确值)。这个值也可能是0。一般情况下不是数据的总量。

再看看read()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* Reads some number of bytes from the input stream and stores them into
* the buffer array <code>b</code>. The number of bytes actually read is
* returned as an integer. This method blocks until input data is
* available, end of file is detected, or an exception is thrown.
*
* <p> If the length of <code>b</code> is zero, then no bytes are read and
* <code>0</code> is returned; otherwise, there is an attempt to read at
* least one byte. If no byte is available because the stream is at the
* end of the file, the value <code>-1</code> is returned; otherwise, at
* least one byte is read and stored into <code>b</code>.
*
* <p> The first byte read is stored into element <code>b[0]</code>, the
* next one into <code>b[1]</code>, and so on. The number of bytes read is,
* at most, equal to the length of <code>b</code>. Let <i>k</i> be the
* number of bytes actually read; these bytes will be stored in elements
* <code>b[0]</code> through <code>b[</code><i>k</i><code>-1]</code>,
* leaving elements <code>b[</code><i>k</i><code>]</code> through
* <code>b[b.length-1]</code> unaffected.
*
* <p> The <code>read(b)</code> method for class <code>InputStream</code>
* has the same effect as: <pre><code> read(b, 0, b.length) </code></pre>
*
* @param b the buffer into which the data is read.
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached.
* @exception IOException If the first byte cannot be read for any reason
* other than the end of the file, if the input stream has been closed, or
* if some other I/O error occurs.
* @exception NullPointerException if <code>b</code> is <code>null</code>.
* @see java.io.InputStream#read(byte[], int, int)
*/
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}

read函数的解释是读取byte数组中的内容,能够读取到多少就读取多少。将实际读取的内容存放在byte数组中。 所以这里也不是你传递数据大小是多少就读取多少的内容。
。分析到这里相信大家明白了我上述例子中代码的问题。那么要实现这个功能有没有比较简单的方法呢?其实是有的。很多的工具类都提供了这个解决方案。例如Apache和Guava。下面提供一种guava的实现方案。具体的实现我就不分析了,有兴趣可以去看看guava的源代码。

guava的实现:

1
2
3
4
5
6
7
8
9
10
11
12
try (InputStream inputStream = httpResponse.getEntity().getContent()) {
byte[] dataContent = ByteStreams.toByteArray(inputStream);
return dataContent;
} catch (Exception e) {
logger.error("download qrcode cause exception", e);
}