阅读stream两次

你怎么读两次相同的inputstream? 是否有可能以某种方式复制它?

我需要从网上获取图像,保存在本地,然后返回保存的图像。 我只是想,使用相同的stream而不是开始一个新的stream到下载的内容,然后再读一遍会更快。

您可以使用org.apache.commons.io.IOUtils.copy将InputStream的内容复制到一个字节数组,然后使用ByteArrayInputStream反复读取字节数组。 例如:

 ByteArrayOutputStream baos = new ByteArrayOutputStream(); org.apache.commons.io.IOUtils.copy(in, baos); byte[] bytes = baos.toByteArray(); // either while (needToReadAgain) { ByteArrayInputStream bais = new ByteArrayInputStream(bytes); yourReadMethodHere(bais); } // or ByteArrayInputStream bais = new ByteArrayInputStream(bytes); while (needToReadAgain) { bais.reset(); yourReadMethodHere(bais); } 

根据InputStream的来源,您可能无法重置它。 您可以使用markSupported()来检查是否支持mark()reset() markSupported()

如果是,您可以调用InputStream上的reset()返回到开头。 如果不是,则需要再次从源读取InputStream。

您可以使用PushbackInputStream封装inputstream。 PushbackInputStream允许未读 (“ 回写 ”)已经读取的字节,所以你可以这样做:

 public class StreamTest { public static void main(String[] args) throws IOException { byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; InputStream originalStream = new ByteArrayInputStream(bytes); byte[] readBytes = getBytes(originalStream, 3); printBytes(readBytes); // prints: 1 2 3 readBytes = getBytes(originalStream, 3); printBytes(readBytes); // prints: 4 5 6 // now let's wrap it with PushBackInputStream originalStream = new ByteArrayInputStream(bytes); InputStream wrappedStream = new PushbackInputStream(originalStream, 10); // 10 means that maximnum 10 characters can be "written back" to the stream readBytes = getBytes(wrappedStream, 3); printBytes(readBytes); // prints 1 2 3 ((PushbackInputStream) wrappedStream).unread(readBytes, 0, readBytes.length); readBytes = getBytes(wrappedStream, 3); printBytes(readBytes); // prints 1 2 3 } private static byte[] getBytes(InputStream is, int howManyBytes) throws IOException { System.out.print("Reading stream: "); byte[] buf = new byte[howManyBytes]; int next = 0; for (int i = 0; i < howManyBytes; i++) { next = is.read(); if (next > 0) { buf[i] = (byte) next; } } return buf; } private static void printBytes(byte[] buffer) throws IOException { System.out.print("Reading stream: "); for (int i = 0; i < buffer.length; i++) { System.out.print(buffer[i] + " "); } System.out.println(); } } 

请注意,PushbackInputStream存储字节的内部缓冲区,所以它真的在内存中创build一个缓冲区,保存字节“写回”。

了解这种方法,我们可以进一步将其与FilterInputStream结合起来。 FilterInputStream将原始inputstream存储为委托。 这允许创build新的类定义,允许自动“ 未读 ”原始数据。 这个类的定义如下:

 public class TryReadInputStream extends FilterInputStream { private final int maxPushbackBufferSize; /** * Creates a <code>FilterInputStream</code> * by assigning the argument <code>in</code> * to the field <code>this.in</code> so as * to remember it for later use. * * @param in the underlying input stream, or <code>null</code> if * this instance is to be created without an underlying stream. */ public TryReadInputStream(InputStream in, int maxPushbackBufferSize) { super(new PushbackInputStream(in, maxPushbackBufferSize)); this.maxPushbackBufferSize = maxPushbackBufferSize; } /** * Reads from input stream the <code>length</code> of bytes to given buffer. The read bytes are still avilable * in the stream * * @param buffer the destination buffer to which read the data * @param offset the start offset in the destination <code>buffer</code> * @aram length how many bytes to read from the stream to buff. Length needs to be less than * <code>maxPushbackBufferSize</code> or IOException will be thrown * * @return number of bytes read * @throws java.io.IOException in case length is */ public int tryRead(byte[] buffer, int offset, int length) throws IOException { validateMaxLength(length); // NOTE: below reading byte by byte instead of "int bytesRead = is.read(firstBytes, 0, maxBytesOfResponseToLog);" // because read() guarantees to read a byte int bytesRead = 0; int nextByte = 0; for (int i = 0; (i < length) && (nextByte >= 0); i++) { nextByte = read(); if (nextByte >= 0) { buffer[offset + bytesRead++] = (byte) nextByte; } } if (bytesRead > 0) { ((PushbackInputStream) in).unread(buffer, offset, bytesRead); } return bytesRead; } public byte[] tryRead(int maxBytesToRead) throws IOException { validateMaxLength(maxBytesToRead); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // as ByteArrayOutputStream to dynamically allocate internal bytes array instead of allocating possibly large buffer (if maxBytesToRead is large) // NOTE: below reading byte by byte instead of "int bytesRead = is.read(firstBytes, 0, maxBytesOfResponseToLog);" // because read() guarantees to read a byte int nextByte = 0; for (int i = 0; (i < maxBytesToRead) && (nextByte >= 0); i++) { nextByte = read(); if (nextByte >= 0) { baos.write((byte) nextByte); } } byte[] buffer = baos.toByteArray(); if (buffer.length > 0) { ((PushbackInputStream) in).unread(buffer, 0, buffer.length); } return buffer; } private void validateMaxLength(int length) throws IOException { if (length > maxPushbackBufferSize) { throw new IOException( "Trying to read more bytes than maxBytesToRead. Max bytes: " + maxPushbackBufferSize + ". Trying to read: " + length); } } } 

这个类有两个方法。 一个用于读入现有的缓冲区(定义类似于调用InputStream类的public int read(byte b[], int off, int len) )。 第二个返回新缓冲区(如果读取的缓冲区的大小未知,这可能会更有效)。

现在让我们看看我们的class级在行动:

 public class StreamTest2 { public static void main(String[] args) throws IOException { byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; InputStream originalStream = new ByteArrayInputStream(bytes); byte[] readBytes = getBytes(originalStream, 3); printBytes(readBytes); // prints: 1 2 3 readBytes = getBytes(originalStream, 3); printBytes(readBytes); // prints: 4 5 6 // now let's use our TryReadInputStream originalStream = new ByteArrayInputStream(bytes); InputStream wrappedStream = new TryReadInputStream(originalStream, 10); readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); // NOTE: no manual call to "unread"(!) because TryReadInputStream handles this internally printBytes(readBytes); // prints 1 2 3 readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); printBytes(readBytes); // prints 1 2 3 readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); printBytes(readBytes); // prints 1 2 3 // we can also call normal read which will actually read the bytes without "writing them back" readBytes = getBytes(wrappedStream, 3); printBytes(readBytes); // prints 1 2 3 readBytes = getBytes(wrappedStream, 3); printBytes(readBytes); // prints 4 5 6 readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); // now we can try read next bytes printBytes(readBytes); // prints 7 8 9 readBytes = ((TryReadInputStream) wrappedStream).tryRead(3); printBytes(readBytes); // prints 7 8 9 } } 

如果您正在使用InputStream接口的实现,则可以检查InputStream#markSupported()的结果,告诉您是否可以使用方法mark() / reset()

如果读取时可以标记stream,则调用reset()返回开始。

如果你不能,你将不得不再次打开一个stream。

另一个解决scheme是将InputStream转换为字节数组,然后在需要的时候迭代整个数组。 你可以在这篇文章中find几个解决scheme使用第三方库将InputStream转换为Java中的字节数组 。 注意,如果读取的内容太大,您可能会遇到一些内存问题。

最后,如果你的需要是读取图像,然后使用:

 BufferedImage image = ImageIO.read(new URL("http://www.example.comhttp://img.dovov.comtoto.jpg")); 

使用ImageIO#read(java.net.URL)也允许你使用caching。

如果你的InputStream支持使用mark,那么你可以mark()你的inputStream然后reset()它。 如果你的InputStrem不支持标记,那么你可以使用类java.io.BufferedInputStream ,所以你可以embedded你的stream在一个BufferedInputStream像这样

  InputStream bufferdInputStream = new BufferedInputStream(yourInputStream); bufferdInputStream.mark(some_value); //read your bufferdInputStream bufferdInputStream.reset(); //read it again 

将inputstream转换为字节,然后将其传递给savefile函数,将其组装到inputstream中。 在原始函数中也使用字节用于其他任务

怎么样:

 if (stream.markSupported() == false) { // lets replace the stream object ByteArrayOutputStream baos = new ByteArrayOutputStream(); IOUtils.copy(stream, baos); stream.close(); stream = new ByteArrayInputStream(baos.toByteArray()); // now the stream should support 'mark' and 'reset' }