203 Stimmen

Wie klont man einen InputStream?

Ich habe einen InputStream, die ich an eine Methode übergeben, um einige Verarbeitung zu tun. Ich werde denselben InputStream in einer anderen Methode verwenden, aber nach der ersten Verarbeitung wird der InputStream innerhalb der Methode geschlossen.

Wie kann ich den InputStream klonen, um ihn an die Methode zu senden, die ihn schließt? Gibt es eine andere Lösung?

EDIT: die Methoden, die den InputStream schließt, ist eine externe Methode aus einer Lib. Ich habe keine Kontrolle über das Schließen oder nicht.

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

249voto

Anthony Accioly Punkte 20616

Wenn Sie nur dieselben Informationen mehr als einmal lesen wollen und die Eingabedaten klein genug sind, um in den Speicher zu passen, können Sie die Daten aus Ihrem InputStream zu einer ByteArrayOutputStream .

Dann können Sie das zugehörige Array von Bytes erhalten und so viele "geklonte" ByteArrayInputStream s wie Sie möchten.

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

Wenn Sie aber wirklich den ursprünglichen Stream offen halten müssen, um neue Daten zu empfangen, dann müssen Sie den externen Aufruf an close() . Sie müssen Folgendes verhindern close() irgendwie angerufen zu werden.

UPDATE (2019):

Seit Java 9 können die mittleren Bits ersetzt werden durch InputStream.transferTo :

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

36voto

Femi Punkte 63590

Sie möchten Apache's CloseShieldInputStream :

Dies ist ein Wrapper, der verhindert, dass der Stream geschlossen wird. Sie würden etwa so vorgehen.

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

13voto

Kaj Punkte 10764

Sie können sie nicht klonen, und wie Sie Ihr Problem lösen werden, hängt von der Datenquelle ab.

Eine Lösung besteht darin, alle Daten aus dem InputStream in ein Byte-Array zu lesen, dann einen ByteArrayInputStream um dieses Byte-Array herum zu erstellen und diesen Input-Stream an Ihre Methode zu übergeben.

Bearbeiten 1: Das heißt, wenn die andere Methode auch die gleichen Daten lesen muss. D.h. Sie wollen den Stream "zurücksetzen".

11voto

Nathan Ryan Punkte 12507

Wenn die aus dem Stream gelesenen Daten groß sind, würde ich empfehlen, einen TeeInputStream von Apache Commons IO zu verwenden. Auf diese Weise können Sie im Wesentlichen die Eingabe replizieren und eine t'd-Pipe als Klon übergeben.

6voto

Diederik Punkte 4716

Das funktioniert vielleicht nicht in allen Situationen, aber ich habe es so gemacht: Ich habe die FilterInputStream Klasse und führen die erforderliche Verarbeitung der Bytes durch, während die externe Lib die Daten liest.

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
    }

}

Dann übergeben Sie einfach eine Instanz von StreamBytesWithExtraProcessingInputStream wo Sie den Eingabestrom eingegeben hätten. Mit dem ursprünglichen Eingabestrom als Konstruktorparameter.

Es sollte beachtet werden, dass dies Byte für Byte funktioniert, also verwenden Sie dies nicht, wenn eine hohe Leistung erforderlich ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X