Java 如何克隆输入流?
我有一个InputStream,我将它传递给一个方法进行一些处理。我将在其他方法中使用相同的InputStream,但是在第一次处理之后,InputStream将在方法中关闭 我如何克隆InputStream以发送给关闭他的方法?还有别的解决办法吗 编辑:关闭InputStream的方法是库中的外部方法。我无法控制是否关门Java 如何克隆输入流?,java,clone,inputstream,Java,Clone,Inputstream,我有一个InputStream,我将它传递给一个方法进行一些处理。我将在其他方法中使用相同的InputStream,但是在第一次处理之后,InputStream将在方法中关闭 我如何克隆InputStream以发送给关闭他的方法?还有别的解决办法吗 编辑:关闭InputStream的方法是库中的外部方法。我无法控制是否关门 private String getContent(HttpURLConnection con) { InputStream content = null;
private String getContent(HttpURLConnection con) {
InputStream content = null;
String charset = "";
try {
content = con.getInputStream();
CloseShieldInputStream csContent = new CloseShieldInputStream(content);
charset = getCharset(csContent);
return IOUtils.toString(content,charset);
} catch (Exception e) {
System.out.println("Error downloading page: " + e);
return null;
}
}
private String getCharset(InputStream content) {
try {
Source parser = new Source(content);
return parser.getEncoding();
} catch (Exception e) {
System.out.println("Error determining charset: " + e);
return "UTF-8";
}
}
你不能克隆它,你将如何解决你的问题取决于数据的来源 一种解决方案是将InputStream中的所有数据读取到字节数组中,然后围绕该字节数组创建ByteArrayInputStream,并将该输入流传递到方法中 编辑1: 也就是说,如果另一个方法也需要读取相同的数据。也就是说,您想要“重置”流。您想要使用Apache的: 这是一个包装器,可以防止流被关闭。你会这样做的
InputStream is = null;
is = getStream(); //obtain the stream
CloseShieldInputStream csis = new CloseShieldInputStream(is);
// call the bad function that does things it shouldn't
badFunction(csis);
// happiness follows: do something with the original input stream
is.read();
如果您只想多次读取相同的信息,并且输入数据足够小,可以将数据从
InputStream
复制到内存中
然后,您可以获得相关的字节数组,并打开任意多个“克隆的”
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Code simulating the copy
// You could alternatively use NIO
// And please, unlike me, do something about the Exceptions :D
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) > -1 ) {
baos.write(buffer, 0, len);
}
baos.flush();
// Open new InputStreams using recorded bytes
// Can be repeated as many times as you wish
InputStream is1 = new ByteArrayInputStream(baos.toByteArray());
InputStream is2 = new ByteArrayInputStream(baos.toByteArray());
但是,如果您确实需要保持原始流打开以接收新数据,则需要跟踪对close()
的外部调用。您需要防止以某种方式调用close()
更新(2019年):
由于Java 9,中间位可以替换为:
如果从流中读取的数据很大,我建议使用ApacheCommons IO中的TeInputStream。通过这种方式,您可以复制输入并将t'd管道作为克隆传递。这可能不适用于所有情况,但我所做的是:我扩展了类,并在外部库读取数据时对字节进行了必要的处理
public class StreamBytesWithExtraProcessingInputStream extends FilterInputStream {
protected StreamBytesWithExtraProcessingInputStream(InputStream in) {
super(in);
}
@Override
public int read() throws IOException {
int readByte = super.read();
processByte(readByte);
return readByte;
}
@Override
public int read(byte[] buffer, int offset, int count) throws IOException {
int readBytes = super.read(buffer, offset, count);
processBytes(buffer, offset, readBytes);
return readBytes;
}
private void processBytes(byte[] buffer, int offset, int readBytes) {
for (int i = 0; i < readBytes; i++) {
processByte(buffer[i + offset]);
}
}
private void processByte(int readByte) {
// TODO do processing here
}
}
公共类StreamBytesWithExtraProcessingInputStream扩展FilterInputStream{
具有ExtraProcessingInputStream(InputStream in)的受保护StreamByTes{
超级(in),;
}
@凌驾
public int read()引发IOException{
int readByte=super.read();
processByte(readByte);
返回readByte;
}
@凌驾
公共整数读取(字节[]缓冲区、整数偏移量、整数计数)引发IOException{
int readBytes=super.read(缓冲区、偏移量、计数);
processBytes(缓冲区、偏移量、读取字节);
返回readBytes;
}
私有void processBytes(字节[]缓冲区、int偏移量、int readBytes){
for(int i=0;i
然后,您只需通过ExtraProcessingInputStream传递一个StreamBytes的实例,您将在其中传递输入流。使用原始输入流作为构造函数参数
需要注意的是,这是一个字节对一个字节的操作,所以如果需要高性能,就不要使用它。下面的类应该可以做到这一点。只需创建一个实例,调用“multiply”方法,并提供源输入流和所需的副本数量
package foo.bar;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class InputStreamMultiplier {
protected static final int BUFFER_SIZE = 1024;
private ExecutorService executorService = Executors.newCachedThreadPool();
public InputStream[] multiply(final InputStream source, int count) throws IOException {
PipedInputStream[] ins = new PipedInputStream[count];
final PipedOutputStream[] outs = new PipedOutputStream[count];
for (int i = 0; i < count; i++)
{
ins[i] = new PipedInputStream();
outs[i] = new PipedOutputStream(ins[i]);
}
executorService.execute(new Runnable() {
public void run() {
try {
copy(source, outs);
} catch (IOException e) {
e.printStackTrace();
}
}
});
return ins;
}
protected void copy(final InputStream source, final PipedOutputStream[] outs) throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
int n = 0;
try {
while (-1 != (n = source.read(buffer))) {
//write each chunk to all output streams
for (PipedOutputStream out : outs) {
out.write(buffer, 0, n);
}
}
} finally {
//close all output streams
for (PipedOutputStream out : outs) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
重要提示:必须在单独的线程中同时使用所有克隆流。
package foo.bar;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class InputStreamMultiplier {
protected static final int BUFFER_SIZE = 1024;
private ExecutorService executorService = Executors.newCachedThreadPool();
public InputStream[] multiply(final InputStream source, int count) throws IOException {
PipedInputStream[] ins = new PipedInputStream[count];
final PipedOutputStream[] outs = new PipedOutputStream[count];
for (int i = 0; i < count; i++)
{
ins[i] = new PipedInputStream();
outs[i] = new PipedOutputStream(ins[i]);
}
executorService.execute(new Runnable() {
public void run() {
try {
copy(source, outs);
} catch (IOException e) {
e.printStackTrace();
}
}
});
return ins;
}
protected void copy(final InputStream source, final PipedOutputStream[] outs) throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
int n = 0;
try {
while (-1 != (n = source.read(buffer))) {
//write each chunk to all output streams
for (PipedOutputStream out : outs) {
out.write(buffer, 0, n);
}
}
} finally {
//close all output streams
for (PipedOutputStream out : outs) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package foo.bar;
导入java.io.IOException;
导入java.io.InputStream;
导入java.io.PipedInputStream;
导入java.io.PipedOutputStream;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
公共类输入流乘法器{
受保护的静态最终整数缓冲区大小=1024;
私有ExecutorService ExecutorService=Executors.newCachedThreadPool();
公共InputStream[]乘法(最终InputStream源,int计数)引发IOException{
PipedInputStream[]ins=新的PipedInputStream[count];
最终PipedOutputStream[]输出=新的PipedOutputStream[count];
for(int i=0;i
UPD。
检查之前的评论。这不完全是被问到的
如果您使用的是apache.commons
,则可以使用IOUtils
复制流
您可以使用以下代码:
InputStream = IOUtils.toBufferedInputStream(toCopy);
以下是适合您的情况的完整示例:
public void cloneStream() throws IOException{
InputStream toCopy=IOUtils.toInputStream("aaa");
InputStream dest= null;
dest=IOUtils.toBufferedInputStream(toCopy);
toCopy.close();
String result = new String(IOUtils.toByteArray(dest));
System.out.println(result);
}
此代码需要一些依赖项:
MAVEN
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
以下是此方法的文档参考:
获取InputStream的全部内容,并表示与之相同的数据
结果输入流。此方法在以下情况下非常有用:
源输入流很慢。它有相关的网络资源,所以我们
不能让它长时间打开。它与网络超时关联
您可以在此处找到有关IOUtils
的更多信息:
克隆一个i
'commons-io:commons-io:2.4'
public class Foo {
private Supplier<InputStream> inputStreamSupplier;
public void bar() {
procesDataThisWay(inputStreamSupplier.get());
procesDataTheOtherWay(inputStreamSupplier.get());
}
private void procesDataThisWay(InputStream) {
// ...
}
private void procesDataTheOtherWay(InputStream) {
// ...
}
}
val inputStream = ...
val byteOutputStream = ByteArrayOutputStream()
inputStream.use { input ->
byteOutputStream.use { output ->
input.copyTo(output)
}
}
val byteInputStream = ByteArrayInputStream(byteOutputStream.toByteArray())