Java - 重置InputStream

21

我正在处理一些Java代码,其中有一个InputStream,我需要在同一个方法中读取两次。

问题是我需要将其位置重置到开头才能读取两次。

我已经找到了一个hack-ish的解决方案:

is.mark(Integer.MAX_VALUE);

//Read the InputStream is fully
// { ... }

try
{
    is.reset();
}
catch (IOException e)
{
    e.printStackTrace();
}

这个解决方案会导致一些意外的行为吗?或者它会在愚蠢的状态下工作?


在读取代码也会“标记(mark())”的情况下,它可能会失败。 - Sotirios Delimanolis
你可以扩展InputStream,覆盖mark方法,以便它只能在实例中被调用一次。 - Cruncher
5个回答

12

写成现在这样,你没有任何保证,因为 mark() 没有必要报告是否成功。要获得保证,必须首先调用markSupported(),并且它必须返回 true

此外,指定的读取限制非常危险。如果你恰好使用了可在内存中缓冲的流,它将可能分配一个 2GB 的缓冲区。另一方面,如果你使用的是 FileInputStream,那么就没问题。

更好的方法是使用带有显式缓冲区的 BufferedInputStream


1
这意味着 BufferedInputStream 可以被遍历两次吗? - iMineLink
1
@iMineLink - 是的,只要你提供足够大的缓冲区。没有什么魔法可以存储字节而不消耗内存。如果这是个问题的话,你需要将数据存储在本地文件中(根据其他评论的假设,我认为你是从套接字中读取数据)。 - kdgregory
我的错误在于没有很好地说明:我正在从由getResourceAsStream(xyz)返回的InputStream中读取,我认为它的作用类似于FileInputStream。因此,我将尝试使用显式缓冲区大小将其包装在BufferedInputStream中。 - iMineLink

3

这取决于InputStream的实现方式。你也可以思考一下,是否使用byte[]会更好。最简单的方法是使用Apache的commons-io

byte[] bytes = IOUtils.toByteArray(inputSream);

byte[] 对我来说太重了,我无法分配...我已经尝试过类似的方法,在一些 Android 设备上出现了 OOM...我应该压缩它吗?如果是这种情况,我该怎么做? - iMineLink
如果您正在使用SocketInputStream,则不能使用mark()多次读取它。如果没有足够的内存将流数据保存为字节数组,可以尝试将InputStream数据重定向到临时文件,然后可以使用BufferedInputStream(支持mark())或RandomAccessFile从该文件中读取数据。 - stan

2

你不能可靠地做到这一点;一些InputStream(例如连接到终端或套接字的流)不支持markreset(请参见markSupported)。如果你确实需要两次遍历数据,则需要将其读入自己的缓冲区。


1

不要试图重置 InputStream,而是将其加载到缓冲区中,例如像 StringBuilder 这样的文本数据流,或者像 ByteArrayOutputStream 这样的二进制数据流。然后,您可以在方法内部多次处理缓冲区。

ByteArrayOutputStream bos = new ByteArrayOutputStream();

int read = 0;
byte[] buff = new byte[1024];
while ((read = inStream.read(buff)) != -1) {
    bos.write(buff, 0, read);
}
byte[] streamData = bos.toByteArray();

1
正如其他类似答案中所评论的那样,byte[] 对我来说太重了无法分配... - iMineLink

1
对我来说,最简单的解决方案是传递可以获取InputStream的对象,并重新获取它。在我的情况下,它来自于ContentResolver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接