如何克隆InputStream?

时间:2011-05-07 20:34:07

标签: java clone inputstream

我有一个InputStream,我传递给一个方法来做一些处理。我将在其他方法中使用相同的InputStream,但在第一次处理之后,InputStream似乎在方法内部关闭。

如何克隆InputStream以发送给关闭他的方法?还有另一种解决方案吗?

编辑:关闭InputStream的方法是来自lib的外部方法。我无法控制关闭与否。

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";
    }
}

10 个答案:

答案 0 :(得分:164)

如果你想要做的只是多次读取相同的信息,并且输入数据足够小以适应内存,你可以将数据从InputStream复制到ByteArrayOutputStream

然后,您可以获取相关的字节数组,并根据需要打开尽可能多的“克隆”ByteArrayInputStream

ByteArrayOutputStream baos = new ByteArrayOutputStream();

// Fake code simulating the copy
// You can generally do better with nio if you need...
// 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 the recorded bytes
// Can be repeated as many times as you wish
InputStream is1 = new ByteArrayInputStream(baos.toByteArray()); 
InputStream is2 = new ByteArrayInputStream(baos.toByteArray()); 

但是如果您确实需要保持原始流开放以接收新数据,那么您将需要跟踪此外部close()方法并防止以某种方式调用它。

更新(2019年):

从Java 9开始,中间位可以替换为InputStream.transferTo

ByteArrayOutputStream baos = new ByteArrayOutputStream();
input.transferTo(baos);
InputStream firstClone = new ByteArrayInputStream(baos.toByteArray()); 
InputStream secondClone = new ByteArrayInputStream(baos.toByteArray()); 

答案 1 :(得分:30)

您想使用Apache的CloseShieldInputStream

这是一个阻止流关闭的包装器。你会做这样的事情。

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

答案 2 :(得分:9)

您无法克隆它,以及您将如何解决问题取决于数据的来源。

一种解决方案是将InputStream中的所有数据读入字节数组,然后围绕该字节数组创建一个ByteArrayInputStream,并将该输入流传递给您的方法。

编辑1: 也就是说,如果另一种方法也需要读取相同的数据。即你想“重置”流。

答案 3 :(得分:7)

如果从流中读取的数据很大,我建议使用Apache Commons IO中的TeeInputStream。这样你就可以基本上复制输入并传递一个t'd管道作为你的克隆。

答案 4 :(得分:5)

这可能不适用于所有情况,但这就是我所做的:我扩展了FilterInputStream类,并在外部lib读取数据时执行所需的字节处理。

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的实例,您将在输入流中传递该实例。使用原始输入流作为构造函数参数。

应该注意,这对于字节是有效的,所以如果需要高性能,请不要使用它。

答案 5 :(得分:3)

如果您使用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>

<强>摇篮

'commons-io:commons-io:2.4'

以下是此方法的DOC参考:

  

获取InputStream的全部内容并表示与之相同的数据   结果InputStream。

表示此方法非常有用      

Source InputStream很慢。它有相关的网络资源,所以我们   不能长时间保持开放状态。它已关联网络超时。

您可以在此处找到有关IOUtils的更多信息: http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)

答案 6 :(得分:0)

克隆输入流可能不是一个好主意,因为这需要深入了解要克隆的输入流的详细信息。解决方法是创建一个新的输入流,再次从同一个源读取。

因此,使用一些Java 8功能,如下所示:

public class Foo {

    private Supplier<InputStream> inputStreamSupplier;

    public void bar() {
        procesDataThisWay(inputStreamSupplier.get());
        procesDataTheOtherWay(inputStreamSupplier.get());
    }

    private void procesDataThisWay(InputStream) {
        // ...
    }

    private void procesDataTheOtherWay(InputStream) {
        // ...
    }
}

这种方法具有积极的作用,它将重用已经存在的代码 - 创建封装在inputStreamSupplier中的输入流。并且不需要为克隆流维护第二个代码路径。

另一方面,如果从流中读取是昂贵的(因为它是通过低带宽连接完成的),那么这种方法将使成本加倍。这可以通过使用特定供应商来规避,该供应商将首先在本地存储流内容,并为现在的本地资源提供InputStream

答案 7 :(得分:0)

以下是Kotlin的解决方案。

您可以将InputStream复制到ByteArray

val inputStream = ...

val byteOutputStream = ByteArrayOutputStream()
inputStream.use { input ->
    byteOutputStream.use { output ->
        input.copyTo(output)
    }
}

val byteInputStream = ByteArrayInputStream(byteOutputStream.toByteArray())

如果您需要多次阅读byteInputStream,请在再次阅读之前致电byteInputStream.reset()

https://code.luasoftware.com/tutorials/kotlin/how-to-clone-inputstream/

答案 8 :(得分:0)

通过示例增强 https://www.khronos.org/webgl/

<块引用>

@Anthony Accioly:克隆 bytes-Stream 并提供副本数作为列表集合。

public static List<InputStream> multiplyBytes(InputStream input, int cloneCount) throws IOException {
    List<InputStream> copies = new ArrayList<InputStream>();
    
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    copy(input, baos);
    
    for (int i = 0; i < cloneCount; i++) {
        copies.add(new ByteArrayInputStream(baos.toByteArray()));
    }
    return copies;
}
// IOException - If reading the Reader or Writing into the Writer goes wrong.
public static void copy(Reader in, Writer out) throws IOException {
    try {
        char[] buffer = new char[1024];
        int nrOfBytes = -1;
        while ((nrOfBytes = in.read(buffer)) != -1) {
            out.write(buffer, 0, nrOfBytes);
        }
        out.flush();
    } finally {
        close(in);
        close(out);
    }
}
<块引用>

InputStream:克隆 chars-Stream 并提供副本数作为列表集合。

public static List<Reader> multiplyChars(Reader reader, int cloneCOunt) throws IOException {
    List<Reader> copies = new ArrayList<Reader>();
    BufferedReader bufferedInput = new BufferedReader(reader);
    StringBuffer buffer = new StringBuffer();
    String delimiter = System.getProperty("line.separator");
    String line;
    while ((line = bufferedInput.readLine()) != null) {
        if (!buffer.toString().equals(""))
            buffer.append(delimiter);
        buffer.append(line);
    }
    close(bufferedInput);
    for (int i = 0; i < cloneCOunt; i++) {
        copies.add(new StringReader(buffer.toString()));
    }
    return copies;
}
public static void copy(InputStream in, OutputStream out) throws IOException {
    try {
        byte[] buffer = new byte[1024];
        int nrOfBytes = -1;
        while ((nrOfBytes = in.read(buffer)) != -1) {
            out.write(buffer, 0, nrOfBytes);
        }
        out.flush();
    } finally {
        close(in);
        close(out);
    }
}

完整示例:

public class SampleTest {

    public static void main(String[] args) throws IOException {
        String filePath = "C:/Yash/StackoverflowSSL.cer";
        InputStream fileStream = new FileInputStream(new File(filePath) );
        
        List<InputStream> bytesCopy = multiplyBytes(fileStream, 3);
        for (Iterator<InputStream> iterator = bytesCopy.iterator(); iterator.hasNext();) {
            InputStream inputStream = (InputStream) iterator.next();
            System.out.println("Byte Stream:"+ inputStream.available()); // Byte Stream:1784
        }
        printInputStream(bytesCopy.get(0));
        
        //java.sql.Clob clob = ((Clob) getValue(sql)); - clob.getCharacterStream();
        Reader stringReader = new StringReader("StringReader that reads Characters from the specified string.");
        List<Reader> charsCopy = multiplyChars(stringReader, 3);
        for (Iterator<Reader> iterator = charsCopy.iterator(); iterator.hasNext();) {
            Reader reader = (Reader) iterator.next();
            System.out.println("Chars Stream:"+reader.read()); // Chars Stream:83
        }
        printReader(charsCopy.get(0));
    }
    
    // Reader, InputStream - Prints the contents of the reader to System.out.
    public static void printReader(Reader reader) throws IOException {
        BufferedReader br = new BufferedReader(reader);
        String s;
        while ((s = br.readLine()) != null) {
            System.out.println(s);
        }
    }
    public static void printInputStream(InputStream inputStream) throws IOException {
        printReader(new InputStreamReader(inputStream));
    }

    // Closes an opened resource, catching any exceptions.
    public static void close(Closeable resource) {
        if (resource != null) {
            try {
                resource.close();
            } catch (IOException e) {
                System.err.println(e);
            }
        }
    }
}

答案 9 :(得分:-1)

下面的课应该可以解决问题。只需创建一个实例,调用“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();
                }
            }
        }
    }
}