[java] 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";
    }
}



답변

당신이 한 번 이상 동일한 정보를 읽어 싶지 모든, 입력 데이터가 메모리에 맞게 충분히 작은 경우, 당신은 당신의 데이터를 복사 할 수 있습니다 InputStreamA를 있는 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()); 


답변

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


답변

복제 할 수 없으며 문제를 해결하는 방법은 데이터 소스에 따라 다릅니다.

한 가지 해결책은 InputStream의 모든 데이터를 바이트 배열로 읽은 다음 해당 바이트 배열 주위에 ByteArrayInputStream을 작성하고 해당 입력 스트림을 메소드에 전달하는 것입니다.

편집 1 : 즉 다른 방법이 동일한 데이터를 읽어야하는 경우입니다. 즉, 스트림을 “재설정”하고 싶습니다.


답변

스트림에서 읽은 데이터가 큰 경우 Apache Commons IO의 TeeInputStream을 사용하는 것이 좋습니다. 그렇게하면 본질적으로 입력을 복제하고 t’d 파이프를 복제본으로 전달할 수 있습니다.


답변

이것은 모든 상황에서 작동하지는 않지만 여기에 내가 한 일이 있습니다. 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입력 스트림에서 전달한 인스턴스를 전달하면 됩니다. 원래 입력 스트림을 생성자 매개 변수로 사용합니다.

이것은 바이트 단위로 작동하므로 고성능이 필요한 경우 이것을 사용하지 마십시오.


답변

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

이 코드에는 몇 가지 종속성이 필요합니다.

메이븐

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>

그래들

'commons-io:commons-io:2.4'

이 방법에 대한 DOC 참조는 다음과 같습니다.

InputStream의 전체 내용을 가져오고 결과 InputStream과 동일한 데이터를 나타냅니다. 이 방법은 다음과 같은 경우에 유용합니다.

소스 입력 스트림이 느립니다. 네트워크 리소스가 연결되어 있으므로 오랫동안 열어 둘 수 없습니다. 네트워크 시간 초과가 연결되어 있습니다.

http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)에 대한 자세한 내용은 IOUtils여기를 참조 하십시오.


답변

다음은 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/