我在开发阶段,在那里我有两个模块,从一个我得到输出作为一个OutputStream和第二个,它只接受InputStream。你知道如何将OutputStream转换为InputStream(反之亦然,我的意思是真的这样),我将能够连接这两个部分吗?
谢谢
我在开发阶段,在那里我有两个模块,从一个我得到输出作为一个OutputStream和第二个,它只接受InputStream。你知道如何将OutputStream转换为InputStream(反之亦然,我的意思是真的这样),我将能够连接这两个部分吗?
谢谢
当前回答
如果你想从一个InputStream生成一个OutputStream,有一个基本的问题。写入OutputStream的方法会阻塞,直到完成为止。因此,当编写方法完成时,结果是可用的。这有两个后果:
如果只使用一个线程,则需要等待所有内容写入(因此需要将流数据存储在内存或磁盘中)。 如果希望在数据完成之前访问数据,则需要第二个线程。
变体1可以使用字节数组或字段实现。 变体1可以使用pipies实现(直接或额外的抽象-例如RingBuffer或来自其他注释的谷歌库)。
事实上,在标准java中,没有其他方法可以解决这个问题。每个解决方案都是其中一个的实现。
有一个概念叫做“延续”(详见维基百科)。在这种情况下,这基本上意味着:
有一个特殊的输出流,它需要一定数量的数据 如果达到数量,则流将控制权交给对应的特殊输入流 输入流在读取数据之前提供可用的数据量,在读取之后,它将控制传递回输出流
虽然有些语言内置了这个概念,但对于java,您需要一些“魔法”。例如apache中的“commons-javaflow”实现了这样的java。缺点是这需要在构建时进行一些特殊的字节码修改。因此,将所有的东西都放在一个带有自定义构建脚本的额外库中是有意义的。
其他回答
我遇到了同样的问题,将ByteArrayOutputStream转换为ByteArrayInputStream,并通过使用ByteArrayOutputStream的派生类来解决它,它能够返回一个ByteArrayInputStream,该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.
* <p>
* The ByteArrayInputStream can be retrieved using <code>getInputStream()</code>.
* @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上
Though you cannot convert an OutputStream to an InputStream, java provides a way using PipedOutputStream and PipedInputStream that you can have data written to a PipedOutputStream to become available through an associated PipedInputStream. Sometime back I faced a similar situation when dealing with third party libraries that required an InputStream instance to be passed to them instead of an OutputStream instance. The way I fixed this issue is to use the PipedInputStream and PipedOutputStream. By the way they are tricky to use and you must use multithreading to achieve what you want. I recently published an implementation on github which you can use. Here is the link . You can go through the wiki to understand how to use it.
正如一些人已经回答的那样,没有有效的方法来“转换”一个OutputStream到一个InputStream。解决这类问题的技巧是将所有需要OutputStream的代码执行到它自己的线程中。通过使用管道流,我们可以将数据从创建的线程传输到InputStream中。
使用示例:
public static InputStream downloadFileAsStream(final String uriString) throws IOException {
final InputStream inputStream = runInOwnThreadWithPipedStreams((outputStream) -> {
try {
downloadUriToStream(uriString, outputStream);
} catch (final Exception e) {
LOGGER.error("Download of uri '{}' has failed", uriString, e);
}
});
return inputStream;
}
辅助功能:
public static InputStream runInOwnThreadWithPipedStreams(
final Consumer<OutputStream> outputStreamConsumer) throws IOException {
final PipedInputStream inputStream = new PipedInputStream();
final PipedOutputStream outputStream = new PipedOutputStream(inputStream);
new Thread(new Runnable() {
public void run() {
try {
outputStreamConsumer.accept(outputStream);
} finally {
try {
outputStream.close();
} catch (final IOException e) {
LOGGER.error("Closing outputStream has failed. ", e);
}
}
}
}).start();
return inputStream;
}
单元测试:
@Test
void testRunInOwnThreadWithPipedStreams() throws IOException {
final InputStream inputStream = LoadFileUtil.runInOwnThreadWithPipedStreams((OutputStream outputStream) -> {
try {
IOUtils.copy(IOUtils.toInputStream("Hello World", StandardCharsets.UTF_8), outputStream);
} catch (final IOException e) {
LoggerFactory.getLogger(LoadFileUtilTest.class).error(e.getMessage(), e);
}
});
final String actualResult = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
Assertions.assertEquals("Hello World", actualResult);
}
库io-extras可能很有用。例如,如果你想使用GZIPOutputStream gzip一个InputStream,并且你希望它同步发生(使用默认的缓冲区大小8192):
InputStream is = ...
InputStream gz = IOUtil.pipe(is, o -> new GZIPOutputStream(o));
请注意,该库具有100%的单元测试覆盖率(当然,这是值得的!),并且位于Maven Central上。Maven依赖项是:
<dependency>
<groupId>com.github.davidmoten</groupId>
<artifactId>io-extras</artifactId>
<version>0.1</version>
</dependency>
一定要查看更新的版本。
输出流是将数据写入其中的流。如果某个模块公开了一个OutputStream,则期望在另一端有一些内容正在读取。
另一方面,公开InputStream的内容表明您将需要侦听此流,并且将有您可以读取的数据。
因此,可以将InputStream连接到OutputStream
InputStream----read——> intermediateBytes[n] ----write----> OutputStream
正如有人提到的,这就是IOUtils的copy()方法让您做的事情。相反的方向是没有意义的。希望这能让你们理解
更新:
当然,我越想这一点,就越能看出这实际上是一个要求。我知道有些评论提到了管道输入/输出流,但还有另一种可能性。
如果公开的输出流是bybyterayoutputstream,那么您总是可以通过调用toByteArray()方法来获得完整的内容。然后,您可以使用ByteArrayInputStream子类创建输入流包装器。这两个是伪流,它们基本上都只是包装一个字节数组。因此,以这种方式使用流在技术上是可行的,但对我来说还是很奇怪……