Как преобразовать OutputStream в InputStream?
Я нахожусь на стадии разработки, где у меня есть два модуля, и с одного я получил выход как OutputStream
и второй, который принимает только InputStream
. Вы знаете, как преобразовать OutputStream
в InputStream
(а не наоборот, я имею в виду именно так), что я смогу подключить эти две части?
благодаря
- Java - заполнитель в текстовом поле
- Параметры запроса URL-адреса в Java
- SwingWorker не обновляет JProgressBar без Thread.sleep () в пользовательской диалоговой панели
- получить системную информацию на уровне ОС
- java.util.zip - Восстанавливающая структура каталогов
- Могу ли я передать массив в качестве аргументов метода с переменными аргументами в Java?
- Как обрабатывать MaxUploadSizeExceededException
- Как преобразовать ASCII-код (0-255) в строку связанного символа?
- Требуется наличие пробелов между publicId и systemId
- Вычислить SHA-1 массива байтов
- repaint () в Java
- Есть ли веская причина использовать «printf» вместо «print» в java?
- Отправка почтового вложения с помощью Java
OutputStream
– это тот, где вы записываете данные. Если какой-либо модуль предоставляет OutputStream
, OutputStream
, что на другом конце есть что-то чтение.
Что-то, что предоставляет InputStream
, с другой стороны, указывает, что вам нужно будет слушать этот stream, и будут данные, которые вы можете прочитать.
Таким образом, можно подключить InputStream
к OutputStream
InputStream----read---> intermediateBytes[n] ----write----> OutputStream
Как заметил кто-то, это то, что позволяет метод copy()
из IOUtils . Не имеет смысла идти другим путем … надеюсь, это имеет смысл
ОБНОВИТЬ:
Конечно, чем больше я думаю об этом, тем больше я вижу, как это на самом деле было бы требованием. Я знаю, что некоторые из комментариев упоминаются в streamах ввода / вывода streamа, но есть и другая возможность.
Если выходной stream, который ByteArrayOutputStream
, является ByteArrayOutputStream
, тогда вы всегда можете получить полное содержимое, вызвав метод toByteArray()
. Затем вы можете создать оболочку входного streamа с помощью подclassа ByteArrayInputStream
. Эти два являются псевдоstreamами, они оба в основном просто обертывают массив байтов. Поэтому использование streamов таким образом, технически возможно, но для меня это все еще очень странно …
Похоже, что есть много ссылок и других подобных материалов, но нет реального кода, использующего каналы. Преимущество использования java.io.PipedInputStream
и java.io.PipedOutputStream
в том, что нет дополнительного потребления памяти. ByteArrayOutputStream.toByteArray()
возвращает копию исходного буфера, поэтому это означает, что все, что у вас есть в памяти, теперь имеет две копии. Затем запись в InputStream
означает, что теперь у вас есть три копии данных.
Код:
// take the copy of the stream and re-write it to an InputStream PipedInputStream in = new PipedInputStream(); final PipedOutputStream out = new PipedOutputStream(in); new Thread(new Runnable() { public void run () { try { // write the original OutputStream to the PipedOutputStream originalByteArrayOutputStream.writeTo(out); } catch (IOException e) { // logging and exception handling should go here } } }).start();
Этот код предполагает, что originalByteArrayOutputStream
является ByteArrayOutputStream
поскольку он обычно является единственным используемым streamом вывода, если только вы не пишете файл. Надеюсь, это поможет! Самое замечательное в том, что, поскольку он находится в отдельном streamе, он также работает параллельно, поэтому все, что потребляет ваш stream ввода, также будет выводиться из вашего старого streamа вывода. Это полезно, потому что буфер может оставаться меньше, и у вас будет меньше латентности и меньше использования памяти.
Вам понадобится промежуточный class, который будет задерживаться между ними. Каждый раз, когда InputStream.read(byte[]...)
, class буферизации заполняет переданный в массиве байтов следующий кусок, переданный из OutputStream.write(byte[]...)
. Поскольку размеры блоков не могут быть одинаковыми, class адаптера должен будет хранить определенную сумму, пока не будет достаточно, чтобы заполнить буфер чтения и / или иметь возможность сохранять переполнение буфера.
В этой статье есть хорошая разбивка нескольких различных подходов к этой проблеме:
http://blog.ostermiller.org/convert-java-outputstream-inputstream
Поскольку streamи ввода и вывода – это только начальная и конечная точки, решение заключается в временном хранении данных в массиве байтов. Поэтому вы должны создать промежуточный ByteArrayOutputStream
, из которого вы создаете byte[]
который используется как вход для нового ByteArrayInputStream
.
public void doTwoThingsWithStream(InputStream inStream, OutputStream outStream){ //create temporary bayte array output stream ByteArrayOutputStream baos = new ByteArrayOutputStream(); doFirstThing(inStream, baos); //create input stream from baos InputStream isFromFirstData = new ByteArrayInputStream(baos.toByteArray()); doSecondThing(isFromFirstData, outStream); }
Надеюсь, поможет.
Библиотека с открытым исходным кодом easystream имеет прямую поддержку для преобразования OutputStream в InputStream: http://io-tools.sourceforge.net/easystream/tutorial/tutorial.html
Они также перечисляют другие параметры: http://io-tools.sourceforge.net/easystream/OutputStream_to_InputStream.html
ByteArrayOutputStream buffer = (ByteArrayOutputStream) aOutputStream; byte[] bytes = buffer.toByteArray(); InputStream inputStream = new ByteArrayInputStream(bytes);
Я столкнулся с той же проблемой при преобразовании ByteArrayOutputStream
в ByteArrayInputStream
и решил его с помощью производного classа из ByteArrayOutputStream
который может вернуть ByteArrayInputStream
который инициализируется внутренним буфером ByteArrayOutputStream
. Таким образом, не используется дополнительная память, а «конверсия» выполняется очень быстро:
package info.whitebyte.utils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; /** * This class extends the ByteArrayOutputStream by * providing a method that returns a new ByteArrayInputStream * which uses the internal byte array buffer. This buffer * is not copied, so no additional memory is used. After * creating the ByteArrayInputStream the instance of the * ByteArrayInOutStream can not be used anymore. * * The ByteArrayInputStream can be retrieved using getInputStream()
. * @author Nick Russler */ public class ByteArrayInOutStream extends ByteArrayOutputStream { /** * Creates a new ByteArrayInOutStream. The buffer capacity is * initially 32 bytes, though its size increases if necessary. */ public ByteArrayInOutStream() { super(); } /** * Creates a new ByteArrayInOutStream, with a buffer capacity of * the specified size, in bytes. * * @param size the initial size. * @exception IllegalArgumentException if size is negative. */ public ByteArrayInOutStream(int size) { super(size); } /** * Creates a new ByteArrayInputStream that uses the internal byte array buffer * of this ByteArrayInOutStream instance as its buffer array. The initial value * of pos is set to zero and the initial value of count is the number of bytes * that can be read from the byte array. The buffer array is not copied. This * instance of ByteArrayInOutStream can not be used anymore after calling this * method. * @return the ByteArrayInputStream instance */ public ByteArrayInputStream getInputStream() { // create new ByteArrayInputStream that respects the current count ByteArrayInputStream in = new ByteArrayInputStream(this.buf, 0, this.count); // set the buffer of the ByteArrayOutputStream // to null so it can't be altered anymore this.buf = null; return in; } }
Я поместил материал в github: https://github.com/nickrussler/ByteArrayInOutStream
Возможно, вам пригодится библиотека io-extras . Например, если вы хотите gzip InputStream
с помощью GZIPOutputStream
и вы хотите, чтобы это произошло синхронно (с использованием размера буфера по умолчанию 8192):
InputStream is = ... InputStream gz = IOUtil.pipe(is, o -> new GZIPOutputStream(o));
Обратите внимание, что библиотека имеет 100% -ное охват тестирования (для чего это стоит!) И находится на Maven Central. Зависимость Maven:
com.github.davidmoten io-extras 0.1
Обязательно проверьте более позднюю версию.
С моей точки зрения, java.io.PipedInputStream / java.io.PipedOutputStream – лучший вариант для considere. В некоторых ситуациях вы можете использовать ByteArrayInputStream / ByteArrayOutputStream. Проблема в том, что вам нужно дублировать буфер для преобразования ByteArrayOutputStream в ByteArrayInputStream. Также ByteArrayOutpuStream / ByteArrayInputStream ограничены 2 ГБ. Вот реализация OutpuStream / InputStream, которую я написал, чтобы обойти ограничения ByteArrayOutputStream / ByteArrayInputStream (код Scala, но легко понятный для разработчиков java):
import java.io.{IOException, InputStream, OutputStream} import scala.annotation.tailrec /** Acts as a replacement for ByteArrayOutputStream * */ class HugeMemoryOutputStream(capacity: Long) extends OutputStream { private val PAGE_SIZE: Int = 1024000 private val ALLOC_STEP: Int = 1024 /** Pages array * */ private var streamBuffers: Array[Array[Byte]] = Array.empty[Array[Byte]] /** Allocated pages count * */ private var pageCount: Int = 0 /** Allocated bytes count * */ private var allocatedBytes: Long = 0 /** Current position in stream * */ private var position: Long = 0 /** Stream length * */ private var length: Long = 0 allocSpaceIfNeeded(capacity) /** Gets page count based on given length * * @param length Buffer length * @return Page count to hold the specified amount of data */ private def getPageCount(length: Long) = { var pageCount = (length / PAGE_SIZE).toInt + 1 if ((length % PAGE_SIZE) == 0) { pageCount -= 1 } pageCount } /** Extends pages array * */ private def extendPages(): Unit = { if (streamBuffers.isEmpty) { streamBuffers = new Array[Array[Byte]](ALLOC_STEP) } else { val newStreamBuffers = new Array[Array[Byte]](streamBuffers.length + ALLOC_STEP) Array.copy(streamBuffers, 0, newStreamBuffers, 0, streamBuffers.length) streamBuffers = newStreamBuffers } pageCount = streamBuffers.length } /** Ensures buffers are bug enough to hold specified amount of data * * @param value Amount of data */ private def allocSpaceIfNeeded(value: Long): Unit = { @tailrec def allocSpaceIfNeededIter(value: Long): Unit = { val currentPageCount = getPageCount(allocatedBytes) val neededPageCount = getPageCount(value) if (currentPageCount < neededPageCount) { if (currentPageCount == pageCount) extendPages() streamBuffers(currentPageCount) = new Array[Byte](PAGE_SIZE) allocatedBytes = (currentPageCount + 1).toLong * PAGE_SIZE allocSpaceIfNeededIter(value) } } if (value < 0) throw new Error("AllocSpaceIfNeeded < 0") if (value > 0) { allocSpaceIfNeededIter(value) length = Math.max(value, length) if (position > length) position = length } } /** * Writes the specified byte to this output stream. The general * contract for
write
is that one byte is written * to the output stream. The byte to be written is the eight * low-order bits of the argumentb
. The 24 * high-order bits ofb
are ignored. ** Subclasses of
OutputStream
must provide an * implementation for this method. * * @param b thebyte
. */ @throws[IOException] override def write(b: Int): Unit = { val buffer: Array[Byte] = new Array[Byte](1) buffer(0) = b.toByte write(buffer) } /** * Writeslen
bytes from the specified byte array * starting at offsetoff
to this output stream. * The general contract forwrite(b, off, len)
is that * some of the bytes in the arrayb
are written to the * output stream in order; elementb[off]
is the first * byte written andb[off+len-1]
is the last byte written * by this operation. ** The
write
method ofOutputStream
calls * the write method of one argument on each of the bytes to be * written out. Subclasses are encouraged to override this method and * provide a more efficient implementation. ** If
b
isnull
, a *NullPointerException
is thrown. ** If
off
is negative, orlen
is negative, or *off+len
is greater than the length of the array *b
, then an IndexOutOfBoundsException is thrown. * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. */ @throws[IOException] override def write(b: Array[Byte], off: Int, len: Int): Unit = { @tailrec def writeIter(b: Array[Byte], off: Int, len: Int): Unit = { val currentPage: Int = (position / PAGE_SIZE).toInt val currentOffset: Int = (position % PAGE_SIZE).toInt if (len != 0) { val currentLength: Int = Math.min(PAGE_SIZE - currentOffset, len) Array.copy(b, off, streamBuffers(currentPage), currentOffset, currentLength) position += currentLength writeIter(b, off + currentLength, len - currentLength) } } allocSpaceIfNeeded(position + len) writeIter(b, off, len) } /** Gets an InputStream that points to HugeMemoryOutputStream buffer * * @return InputStream */ def asInputStream(): InputStream = { new HugeMemoryInputStream(streamBuffers, length) } private class HugeMemoryInputStream(streamBuffers: Array[Array[Byte]], val length: Long) extends InputStream { /** Current position in stream * */ private var position: Long = 0 /** * Reads the next byte of data from the input stream. The value byte is * returned as anint
in the range0
to *255
. If no byte is available because the end of the stream * has been reached, the value-1
is returned. This method * blocks until input data is available, the end of the stream is detected, * or an exception is thrown. * *A subclass must provide an implementation of this method. * * @return the next byte of data, or
-1
if the end of the * stream is reached. */ @throws[IOException] def read: Int = { val buffer: Array[Byte] = new Array[Byte](1) if (read(buffer) == 0) throw new Error("End of stream") else buffer(0) } /** * Reads up tolen
bytes of data from the input stream into * an array of bytes. An attempt is made to read as many as *len
bytes, but a smaller number may be read. * The number of bytes actually read is returned as an integer. * *This method blocks until input data is available, end of file is * detected, or an exception is thrown. * *
If
len
is zero, then no bytes are read and *0
is returned; otherwise, there is an attempt to read at * least one byte. If no byte is available because the stream is at end of * file, the value-1
is returned; otherwise, at least one * byte is read and stored intob
. * *The first byte read is stored into element
b[off]
, the * next one intob[off+1]
, and so on. The number of bytes read * is, at most, equal tolen
. Let k be the number of * bytes actually read; these bytes will be stored in elements *b[off]
throughb[off+
k-1]
, * leaving elementsb[off+
k]
through *b[off+len-1]
unaffected. * *In every case, elements
b[0]
through *b[off]
and elementsb[off+len]
through *b[b.length-1]
are unaffected. * *The
read(b,
off,
len)
method * for classInputStream
simply calls the method *read()
repeatedly. If the first such call results in an *IOException
, that exception is returned from the call to * theread(b,
off,
len)
method. If * any subsequent call toread()
results in a *IOException
, the exception is caught and treated as if it * were end of file; the bytes read up to that point are stored into *b
and the number of bytes read before the exception * occurred is returned. The default implementation of this method blocks * until the requested amount of input datalen
has been read, * end of file is detected, or an exception is thrown. Subclasses are encouraged * to provide a more efficient implementation of this method. * * @param b the buffer into which the data is read. * @param off the start offset in arrayb
* at which the data is written. * @param len the maximum number of bytes to read. * @return the total number of bytes read into the buffer, or *-1
if there is no more data because the end of * the stream has been reached. * @see java.io.InputStream#read() */ @throws[IOException] override def read(b: Array[Byte], off: Int, len: Int): Int = { @tailrec def readIter(acc: Int, b: Array[Byte], off: Int, len: Int): Int = { val currentPage: Int = (position / PAGE_SIZE).toInt val currentOffset: Int = (position % PAGE_SIZE).toInt val count: Int = Math.min(len, length - position).toInt if (count == 0 || position >= length) acc else { val currentLength = Math.min(PAGE_SIZE - currentOffset, count) Array.copy(streamBuffers(currentPage), currentOffset, b, off, currentLength) position += currentLength readIter(acc + currentLength, b, off + currentLength, len - currentLength) } } readIter(0, b, off, len) } /** * Skips over and discardsn
bytes of data from this input * stream. Theskip
method may, for a variety of reasons, end * up skipping over some smaller number of bytes, possibly0
. * This may result from any of a number of conditions; reaching end of file * beforen
bytes have been skipped is only one possibility. * The actual number of bytes skipped is returned. Ifn
is * negative, theskip
method for classInputStream
always * returns 0, and no bytes are skipped. Subclasses may handle the negative * value differently. * * Theskip
method of this class creates a * byte array and then repeatedly reads into it untiln
bytes * have been read or the end of the stream has been reached. Subclasses are * encouraged to provide a more efficient implementation of this method. * For instance, the implementation may depend on the ability to seek. * * @param n the number of bytes to be skipped. * @return the actual number of bytes skipped. */ @throws[IOException] override def skip(n: Long): Long = { if (n < 0) 0 else { position = Math.min(position + n, length) length - position } } } }
Простота использования, отсутствие дублирования буфера, ограничение на 2 ГБ памяти
val out: HugeMemoryOutputStream = new HugeMemoryOutputStream(initialCapacity /*may be 0*/) out.write(...) ... val in1: InputStream = out.asInputStream() in1.read(...) ... val in2: InputStream = out.asInputStream() in2.read(...) ...
Если вы хотите создать OutputStream из InputStream, есть одна основная проблема. Метод, записывающий в блок OutputStream до тех пор, пока он не будет выполнен. Таким образом, результат доступен, когда метод записи завершен. Это имеет 2 последствия:
- Если вы используете только один stream, вам нужно подождать, пока все не будет записано (поэтому вам нужно сохранить данные streamа в памяти или на диске).
- Если вы хотите получить доступ к данным до их завершения, вам понадобится второй stream.
Вариант 1 может быть реализован с использованием байтовых массивов или поданных. Вариант 1 может быть реализован с использованием pipies (либо напрямую, либо с дополнительной абстракцией – например, RingBuffer или google lib из другого комментария).
Действительно, со стандартным java нет другого способа решить проблему. Каждое решение является реализацией одного из них.
Существует одна концепция под названием «продолжение» (подробности см. В wikipedia ). В этом случае в основном это означает:
- существует специальный выходной stream, который ожидает определенный объем данных
- если количество достигнуто, stream дает управление своему аналогу, который является специальным входным streamом
- входной stream делает доступным объем данных до его считывания, после чего он передает управление в выходной stream
Хотя на некоторых языках встроена эта концепция, для java вам требуется некоторая «магия». Например, «commons-javaflow» из apache реализует такие для java. Недостатком является то, что это требует некоторых специальных модификаций байт-кода во время сборки. Поэтому было бы целесообразно разместить все материалы в дополнительной библиотеке с помощью скриптов пользовательской сборки.
Старый пост, но может помочь другим, Используйте этот способ:
OutputStream out = new ByteArrayOutputStream(); ... out.write(); ... ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(out.toString().getBytes()));
Хотя вы не можете преобразовать OutputStream в InputStream, java предоставляет способ использования PipedOutputStream и PipedInputStream, чтобы вы могли иметь данные, записанные в PipedOutputStream, чтобы стать доступными через связанный PipedInputStream.
Некоторое время назад я столкнулся с аналогичной ситуацией при работе с сторонними библиотеками, которым требуется передать экземпляр InputStream вместо экземпляра OutputStream.
Способ, которым я исправил эту проблему, – использовать PipedInputStream и PipedOutputStream.
Кстати, они сложны в использовании, и вы должны использовать multithreading для достижения того, чего хотите. Недавно я опубликовал реализацию на github, которую вы можете использовать.
Вот ссылка . Вы можете пройти через wiki, чтобы понять, как его использовать.