如何反复读取同一个 InputStream 对象
我们知道,每次从 InputStream 对象中读取数据后,其当前读取位置的指针就会发生移动。如果在读取完的某时还想重新从此 InputStream 对象中读取数据,但此指针已到尽头,因此无法做到这一点。另外,InputStream 还不支持克隆,这意味着也不能事先备份这个 InputStream 对象。不过,这并不是没有办法。
方法 1
如果使用的 InputStream 对象支持方法 mark,可以联合方法 reset 来进行 InputStream 对象的读取重置。方法是,先使用方法 mark 标记一个位置,然后之后在需要重置的时候,使用方法 reset 来将此 InputStream 对象重置到刚才的位置。
方法 mark 的使用规则如下。当 InputStream 对象调用方法 mark 时,它会在此 InputStream 对象的当前读取位置做一个标记。不过,它需要提供一个参数,这个参数是一个读取范围。在标记后继续从 InputStream 中读取数据时,如果读取到的数据大小超过了这个范围,则之后调用方法 reset 时将直接抛出异常。可以看出,在此处,这个参数没有太大的作用,最好设置为大于需要读取的数据大小,一般设置为在此 InputStream 对象的方法 available 的返回值即可。
使用模板如下:
// 如果此 inputStream 支持方法 mark
if (inputStream.markSupported()) {
try {
inputStream.mark(imageStream.available()); // 在当前位置作标记
} catch (IOException ioException) {
ioException.printStackTrace(); // TODO:处理异常
}
// TODO:从此 InputStream 对象中读取数据
try {
inputStream.reset(); // 将 inputStream 重置
} catch (IOException ioException) {
ioException.printStackTrace(); // TODO:处理异常
}
// TODO:重新开始从此 InputStream 对象中读取数据
try {
inputStream.reset(); // 将 inputStream 再次进行重置,以供以后可能进行的再次读取
} catch (IOException ioException) {
ioException.printStackTrace(); // TODO:处理异常
}
} else {
// TODO:如果不支持方法 mark,使用其它的办法
}
方法 2
如果使用的 InputStream 对象不支持方法 mark(比如,FileInputStream 就不支持此方法),可以考虑使用经典的方法,这种方法类似于深克隆的通用方法。可以先将此 InputStream 对象转化为另一种只读的独立的数据类型,然后再用此数据类型不断生成所需的 InputStream 对象。这种方法有很多,比方说,先将 InputStream 对象转化为 byte 数组,然后创建一个 InputStream 对象来读取此 byte 数组。但是,这种方法需要在一开始就从 InputStream 中读取完全部的数据,这样就会失去 InputStream 对象惰性读取的优势,因此最好使用上面的 方法 1
。这里给出借助 byte 数组来完成这一功能的办法。
byte 数组与 InputStream 的相互转化
byte 数组转 InputStream
public static byte[] inputStream2byteArray(InputStream inputStream) throws IOException {
return inputStream.readAllBytes();
}
InputStream转 byte 数组
public static InputStream byteArray2InputStream(byte[] bytes) {
return new ByteArrayInputStream(bytes);
}
实现反复读 InputStream
byte[] bytes = new byte[0];
try {
bytes = inputStream2byteArray(inputStream);
} catch (IOException ioException) {
ioException.printStackTrace(); // TODO:处理异常
}
// TODO:当需要 InputStream 时,可以直接获得一个新的 InputStream。
var needInputStream = byteArray2InputStream(bytes);
// TODO:反复获得 InputStream
var needInputStream2 = byteArray2InputStream(bytes);
// TODO:...