问题 Java - 重置InputStream


我正在处理一些Java代码,其中有一个我读过一次的InputStream,然后我需要在同一个方法中再次读取它。

问题是我需要将它的位置重置为开头才能读取它两次。

我找到了解决问题的黑客解决方案:

is.mark(Integer.MAX_VALUE);

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

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

这个解决方案会导致一些未经预料到的行为吗?或者它会愚蠢吗?


4290
2017-09-13 19:29


起源

在读取代码的情况下,它可能会失败 mark() 它也是。 - Sotirios Delimanolis
您可以扩展InputStream,覆盖mark方法,以便只能在实例中调用一次。 - Cruncher


答案:


如上所述,您无法保证,因为 mark() 不要求报告是否成功。要获得保证,您必须先致电 的markSupported(),它必须返回 true

同样如上所述,指定的读取限制非常危险。如果您碰巧使用缓冲内存的流,则可能会分配2GB缓冲区。另一方面,如果您碰巧使用了 FileInputStream, 你没事。

更好的方法是使用a BufferedInputStream 使用显式缓冲区。


6
2017-09-13 19:39



这意味着BufferedInputStream可以遍历两次? - iMineLink
@iMineLink - 是的,前提是你给它一个足够大的缓冲区。在没有消耗内存的情况下,没有什么可以神奇地存储字节。如果这是一个问题,您需要将数据存储在本地文件中(我假设,基于其他注释,您正在从套接字读取)。 - kdgregory
我的错是没有很好地指定它:我正在读取由getResourceAsStream(xyz)返回的InputStream,我认为它充当FileInputStream。所以我会尝试将它包装在具有显式缓冲区大小的BufferedInputStream中。 - iMineLink


你不能可靠地做到这一点;一些 InputStreams(例如连接到终端或插座的那些)不支持 mark 和 reset (看到 markSupported)。如果你真的必须遍历数据两次,你需要将它读入你自己的缓冲区。


2
2017-09-13 19:37





它取决于InputStream实现。您还可以考虑使用byte []是否会更好。最简单的方法是使用Apache 公地IO

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

1
2017-09-13 19:38



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


而不是试图重置 InputStream 将它加载到缓冲区中 StringBuilder 或者如果它是二进制数据流a 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
2017-09-13 19:39



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


对我来说,最简单的解决方案是传递可以从中获取InputStream的对象,然后再次获取它。就我而言,它来自一个 ContentResolver


0
2018-05-14 08:10