문제

파일의 입력 스트림이 있으며 Apache POI 구성 요소를 사용하여 다음과 같이 읽습니다.

POIFSFileSystem fileSystem = new POIFSFileSystem(inputStream);

문제는 동일한 스트림을 여러 번 사용해야하고 PoifSfilesystem이 사용 후 스트림을 닫는다는 것입니다.

입력 스트림에서 데이터를 캐시한 다음 다른 PoifSfilesystem에 더 많은 입력 스트림을 제공하는 가장 좋은 방법은 무엇입니까?

편집 1 :

캐시에 의해 나는 응용 프로그램의 속도를 높이는 방법이 아니라 나중에 사용하기위한 저장소를 의미했습니다. 또한 입력 스트림을 배열 또는 문자열로 읽은 다음 각 사용에 대한 입력 스트림을 만드는 것이 더 낫습니까?

편집 2 :

질문을 다시 열어서 죄송하지만 데스크탑 및 웹 응용 프로그램 내에서 작업 할 때 조건이 다소 다릅니다. 우선, Tomcat 웹 앱의 org.apache.commons.fileupload.fileitem에서 얻은 입력 스트림은 마킹을 지원하지 않으므로 재설정 할 수 없습니다.

둘째, 파일을 처리 할 때 파일을 더 빠른 ACC 및 적은 IO 문제로 메모리에 유지할 수 있기를 원합니다.

도움이 되었습니까?

해결책

입력 스트림을 장식 할 수 있습니다 poifsfilesystem close ()라고 불리는 버전으로 restet ()로 응답합니다.

class ResetOnCloseInputStream extends InputStream {

    private final InputStream decorated;

    public ResetOnCloseInputStream(InputStream anInputStream) {
        if (!anInputStream.markSupported()) {
            throw new IllegalArgumentException("marking not supported");
        }

        anInputStream.mark( 1 << 24); // magic constant: BEWARE
        decorated = anInputStream;
    }

    @Override
    public void close() throws IOException {
        decorated.reset();
    }

    @Override
    public int read() throws IOException {
        return decorated.read();
    }
}

테스트 케이스

static void closeAfterInputStreamIsConsumed(InputStream is)
        throws IOException {
    int r;

    while ((r = is.read()) != -1) {
        System.out.println(r);
    }

    is.close();
    System.out.println("=========");

}

public static void main(String[] args) throws IOException {
    InputStream is = new ByteArrayInputStream("sample".getBytes());
    ResetOnCloseInputStream decoratedIs = new ResetOnCloseInputStream(is);
    closeAfterInputStreamIsConsumed(decoratedIs);
    closeAfterInputStreamIsConsumed(decoratedIs);
    closeAfterInputStreamIsConsumed(is);
}

편집 2

바이트 [] (Slurp Mode)에서 전체 파일을 읽은 다음 BytearRayInputStream으로 전달할 수 있습니다.

다른 팁

BufferedInputStream을 사용해보십시오. 다른 입력 스트림에 마크 및 재설정 기능을 추가하고 가까운 방법을 무시합니다.

public class UnclosableBufferedInputStream extends BufferedInputStream {

    public UnclosableBufferedInputStream(InputStream in) {
        super(in);
        super.mark(Integer.MAX_VALUE);
    }

    @Override
    public void close() throws IOException {
        super.reset();
    }
}

그래서:

UnclosableBufferedInputStream  bis = new UnclosableBufferedInputStream (inputStream);

그리고 사용 bis 입력 스트림이 이전에 사용되었던 곳.

이것은 올바르게 작동합니다.

byte[] bytes = getBytes(inputStream);
POIFSFileSystem fileSystem = new POIFSFileSystem(new ByteArrayInputStream(bytes));

GetBytes가 다음과 같습니다.

private static byte[] getBytes(InputStream is) throws IOException {
    byte[] buffer = new byte[8192];
ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
int n;
baos.reset();

while ((n = is.read(buffer, 0, buffer.length)) != -1) {
      baos.write(buffer, 0, n);
    }

   return baos.toByteArray();
 }

더 많은 사용자 정의 사용을 위해 아래 구현을 사용하십시오.

public class ReusableBufferedInputStream extends BufferedInputStream
{

    private int totalUse;
    private int used;

    public ReusableBufferedInputStream(InputStream in, Integer totalUse)
    {
        super(in);
        if (totalUse > 1)
        {
            super.mark(Integer.MAX_VALUE);
            this.totalUse = totalUse;
            this.used = 1;
        }
        else
        {
            this.totalUse = 1;
            this.used = 1;
        }
    }

    @Override
    public void close() throws IOException
    {
        if (used < totalUse)
        {
            super.reset();
            ++used;
        }
        else
        {
            super.close();
        }
    }
}

"캐시"는 정확히 무엇을 의미합니까? 스트림의 시작 부분에서 다른 poifsfilesystem이 시작되기를 원하십니까? 그렇다면 Java 코드에 아무것도 캐싱하는 것은 전혀 없습니다. OS에 의해 수행되며 새 스트림을 열면됩니다.

아니면 첫 번째 poifsfilesystem이 멈춘 시점에서 계속 읽을 수 있습니까? 그것은 캐싱이 아니며,하기가 매우 어렵습니다. 스트림이 닫히지 않으면 내가 생각할 수있는 유일한 방법은 읽은 바이트 수를 계산 한 다음 새 스트림을 열고 많은 바이트를 건너 뛰는 얇은 래퍼를 작성하는 것입니다. 그러나 poifsfilesystem이 내부적으로 BufferedInputStream과 같은 것을 사용하면 실패 할 수 있습니다.

파일이 그렇게 크지 않으면 byte[] 배열 및 POI a ByteArrayInputStream 그 배열에서 만들어졌습니다.

파일이 크면 OS가 최선을 다해 캐싱을 수행하기 때문에 신경 쓰지 않아야합니다.

편집] 사용 Apache Commons-Io 효율적인 방식으로 파일을 바이트 배열로 읽으려면. 사용하지 마세요 int read() 바이트 바이트 바이트를 읽기 때문에 매우 느린!

직접하고 싶다면 File 개체 길이를 얻으려면 파일에서 바이트를 읽는 배열과 루프를 만듭니다. 그 이후로 루프해야합니다 read(byte[], int offset, int len) 보다 적은 것을 읽을 수 있습니다 len 바이트 (일반적으로 그렇습니다).

이것이 제가 구현하는 방법입니다.

  • 원래 스트림 컨텐츠를 미러링하기 위해 임시 파일을 작성하는 직접 입력 스트림 래퍼 작성
  • 원래 입력 스트림 에서이 임시 파일로 읽은 모든 것을 버리십시오.
  • 스트림이 완전히 읽히면 임시 파일에 모든 데이터가 반영됩니다.
  • inputStream.Reset을 사용하여 스위치 (초기화) 내부 스트림을 fileInputStream (mirrored_content_file)으로 향하게합니다.
  • 이제부터 당신은 원래 스트림의 참조를 잃게됩니다 (수집 가능).
  • 임시 파일을 제거하고 열린 스트림을 해제하는 새 메소드 릴리스 ()를 추가하십시오.
  • release ()에서 호출 할 수도 있습니다 마무리하십시오 Release () 호출을 잊어 버린 경우 임시 파일이 릴리스되는지 확인하려면 (대부분 사용하지 않아야합니다. 마무리하십시오, 항상 메소드를 호출하여 객체 자원을 해제하십시오). 보다 Finalize ()를 구현 한 이유는 무엇입니까?
public static void main(String[] args) throws IOException {
    BufferedInputStream inputStream = new BufferedInputStream(IOUtils.toInputStream("Foobar"));
    inputStream.mark(Integer.MAX_VALUE);
    System.out.println(IOUtils.toString(inputStream));
    inputStream.reset();
    System.out.println(IOUtils.toString(inputStream));
}

이것은 작동합니다. Ioutils는 Commons IO의 일부입니다.

이 대답은 이전의 답변에 반복됩니다 1|2 를 기반으로 BufferInputStream. 주요 변화는 무한 재사용을 허용한다는 것입니다. 원래 소스 입력 스트림을 프리 업 시스템 리소스로 닫는 것을 관리합니다. OS는 그에 대한 한도를 정의하고 프로그램이 파일 핸들이 부족하기를 원하지 않습니다 (그것이 또한 당신이 항상 Apache와 함께 '소비'응답을 해야하는 이유이기도합니다. EntityUtils.consumeQuietly()). 편집하다 사용하는 Gready 소비자를 위해 처리 할 코드를 업데이트했습니다. read(buffer, offset, length), 이 경우 발생할 수 있습니다 BufferedInputStream 이 코드는 소스를보기 위해 열심히 노력합니다.이 코드는 해당 용도로부터 보호합니다.

public class CachingInputStream extends BufferedInputStream {    
    public CachingInputStream(InputStream source) {
        super(new PostCloseProtection(source));
        super.mark(Integer.MAX_VALUE);
    }

    @Override
    public synchronized void close() throws IOException {
        if (!((PostCloseProtection) in).decoratedClosed) {
            in.close();
        }
        super.reset();
    }

    private static class PostCloseProtection extends InputStream {
        private volatile boolean decoratedClosed = false;
        private final InputStream source;

        public PostCloseProtection(InputStream source) {
            this.source = source;
        }

        @Override
        public int read() throws IOException {
            return decoratedClosed ? -1 : source.read();
        }

        @Override
        public int read(byte[] b) throws IOException {
            return decoratedClosed ? -1 : source.read(b);
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            return decoratedClosed ? -1 : source.read(b, off, len);
        }

        @Override
        public long skip(long n) throws IOException {
            return decoratedClosed ? 0 : source.skip(n);
        }

        @Override
        public int available() throws IOException {
            return source.available();
        }

        @Override
        public void close() throws IOException {
            decoratedClosed = true;
            source.close();
        }

        @Override
        public void mark(int readLimit) {
            source.mark(readLimit);
        }

        @Override
        public void reset() throws IOException {
            source.reset();
        }

        @Override
        public boolean markSupported() {
            return source.markSupported();
        }
    }
}

재사용하려면 먼저 닫으십시오.

그러나 원래 스트림의 전체 내용이 읽히기 전에 스트림이 닫히면이 데코레이터는 불완전한 데이터를 가지므로 닫기 전에 전체 스트림을 읽어야한다는 것입니다.

나는 이것이 나에게 효과가 있으므로 여기에 내 솔루션을 추가합니다. 기본적으로 상위 두 가지 답변의 조합입니다 :)

    private String convertStreamToString(InputStream is) {
    Writer w = new StringWriter();
    char[] buf = new char[1024];
    Reader r;
    is.mark(1 << 24);
    try {
        r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
        int n;
        while ((n=r.read(buf)) != -1) {
            w.write(buf, 0, n);
        }
        is.reset();
    } catch(UnsupportedEncodingException e) {
        Logger.debug(this.getClass(), "Cannot convert stream to string.", e);
    } catch(IOException e) {
        Logger.debug(this.getClass(), "Cannot convert stream to string.", e);
    }
    return w.toString();
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top