Прочитайте поток в два раза
Как Вы читаете один и тот же входной поток дважды? Можно ли его как-то скопировать?
Мне нужно получить изображение с веб, сохранить его локально, а затем вернуть сохраненное изображение. Я просто подумал, что было бы быстрее использовать тот же поток вместо того, чтобы запускать новый поток в загруженный контент, а затем читать его снова.
7 ответов:
можно использовать
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); }
в зависимости от того, откуда поступает входной поток, вы не сможете его сбросить. Вы можете проверить, если
mark()иreset()поддерживаются с помощьюmarkSupported().если это так, вы можете позвонить
reset()на InputStream, чтобы вернуться к началу. Если нет, вам нужно снова прочитать входной поток из источника.
вы можете обернуть входной поток с PushbackInputStream. 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 хранит внутренний буфер байтов, поэтому он действительно создает буфер в памяти, который содержит байты "записанные назад".
зная этот подход, мы можем пойти дальше и объединить его с FilterInputStream. FilterInputStream магазинах исходный входной поток в качестве делегата. Это позволяет создать новое определение класса, которое позволяет "прочитано" исходные данные автоматически. Определение этого класса следующее:
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); } } }этот класс имеет два метода. Один для чтения в существующий буфер (определение аналогично вызову
public int read(byte b[], int off, int len)класса InputStream). Во-вторых, который возвращает новый буфер (это может быть более эффективным, если размер буфера для чтения неизвестен).теперь давайте посмотрим наш класс в действии:
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поддержка с помощью метки, то вы можетеmark()ваш входной поток, а затемreset()его . если вашInputStremне поддерживает Марк, то вы можете использовать классjava.io.BufferedInputStream,так что вы можете вставлять свой поток внутриBufferedInputStreamтакойInputStream bufferdInputStream = new BufferedInputStream(yourInputStream); bufferdInputStream.mark(some_value); //read your bufferdInputStream bufferdInputStream.reset(); //read it again
если вы используете реализацию
InputStream, вы можете проверить результатInputStream#markSupported()это говорит вам, можете ли вы использовать методmark()/reset().если вы можете отметить поток, когда вы читаете, то позвоните
reset()чтобы вернуться к началу.если вы не можете, вам придется снова открыть поток.
другим решением было бы преобразовать InputStream в массив байтов, а затем выполнить итерацию по массиву столько раз, сколько вам нужно. Вы можете найти несколько решений в этом посте преобразование InputStream в массив байтов в Java использование сторонних библиотек или нет. Внимание, если прочитанное содержимое слишком велико, у вас могут возникнуть некоторые проблемы с памятью.
наконец, если вам нужно прочитать изображение, то используйте:
BufferedImage image = ImageIO.read(new URL("http://www.example.com/images/toto.jpg"));используя
ImageIO#read(java.net.URL)также позволяет использовать кеш.
преобразование 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' }
Comments