Java 如何克隆输入流?

Java 如何克隆输入流?,java,clone,inputstream,Java,Clone,Inputstream,我有一个InputStream,我将它传递给一个方法进行一些处理。我将在其他方法中使用相同的InputStream,但是在第一次处理之后,InputStream将在方法中关闭 我如何克隆InputStream以发送给关闭他的方法?还有别的解决办法吗 编辑:关闭InputStream的方法是库中的外部方法。我无法控制是否关门 private String getContent(HttpURLConnection con) { InputStream content = null;

我有一个InputStream,我将它传递给一个方法进行一些处理。我将在其他方法中使用相同的InputStream,但是在第一次处理之后,InputStream将在方法中关闭

我如何克隆InputStream以发送给关闭他的方法?还有别的解决办法吗

编辑:关闭InputStream的方法是库中的外部方法。我无法控制是否关门

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())