获取到一个Inputstream
后,可能要多次利用它进行 read操作。由于流读过一次就不能再读了,而InputStream
对象本身不能复制,而且它也没有实现Cloneable
接口,所以得想点办法。
实现思路
- 先把
InputStream
转化成ByteArrayOutputStream
- 后面要使用
InputStream
对象时,再从ByteArrayOutputStream
转化回来
代码实现
这种适用于一些不是很大的流,因为缓存流是会消耗内存
package net.gaox;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class StreamOperateUtil {
public static void main(String[] args) throws FileNotFoundException {
InputStream input = new FileInputStream("/home/conf.json");
// 这里可以写你获取到的流
ByteArrayOutputStream baos = cloneInputStream(input);
// 打开两个新的输入流
InputStream stream1 = new ByteArrayInputStream(baos.toByteArray());
InputStream stream2 = new ByteArrayInputStream(baos.toByteArray());
}
private static ByteArrayOutputStream cloneInputStream(InputStream input) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) > -1) {
baos.write(buffer, 0, len);
}
baos.flush();
return baos;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
Java的InputStream为什么要设计为不能重复读?
在InputStream的read方法的注释上明确说明:
Reads some number of bytes from the input stream and stores them into the buffer array b. 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. If the length of b is zero, then no bytes are read and 0 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 -1 is returned; otherwise, at least one byte is read and stored into b. The first byte read is stored into element b[0], the next one into b[1], and so on. The number of bytes read is, at most, equal to the length of b. Let k be the number of bytes actually read; these bytes will be stored in elements b[0] through b[k-1], leaving elements b[k] through b[b.length-1] unaffected.
抽象化来讲InputStream就类比成加特林的子弹链条,链条上的的子弹就像InputStream
里的数据,你把链条上的子弹打出去,这个位置的子弹就没有了,当所有的子弹打光,这个链条也废了,InputStream也是同样的道理。
从更深的代码角度去分析,在InputStream
读取的时候,会有一个pos指针,他指示每次读取之后下一次要读取的起始位置,当读到最后一个字符的时候,pos指针不会重置。就是说InputStream的读取是单向的。
Java 的List内部是使用数组实现的,遍历的时候也有一个pos指针,但是没有说List遍历一个第二次遍历就没有了。第二次遍历是创建新的Iterator,所以pos也回到了数组起始位置。
对于某些InputStream当然可以也这么做。例如ByteArrayInputStream就是将一个Java的byte数组保存到对象里,然后读取的时候遍历该byte数组。就ByteArrayInputStream而言,要实现重复读取是很简单的,但是为什么没有。只是为了遵循InputStream
的统一标准。
BufferedInputStream
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
FileInputStream
public native int read() throws IOException;
ByteArrayInputStream
public ByteArrayInputStream(byte buf[]) {
this.buf = buf;
this.pos = 0;
this.count = buf.length;
}
public synchronized int read() {
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}