How to Cache InputStream for Multiple Use

后端 未结 10 1033
庸人自扰
庸人自扰 2020-11-29 05:57

I have an InputStream of a file and i use apache poi components to read from it like this:

POIFSFileSystem fileSystem = new POIFSFileSystem(inputStream);
         


        
10条回答
  •  栀梦
    栀梦 (楼主)
    2020-11-29 06:27

    This answer iterates on previous ones 1|2 based on the BufferInputStream. The main changes are that it allows infinite reuse. And takes care of closing the original source input stream to free-up system resources. Your OS defines a limit on those and you don't want the program to run out of file handles (That's also why you should always 'consume' responses e.g. with the apache EntityUtils.consumeQuietly()). EDIT Updated the code to handle for gready consumers that use read(buffer, offset, length), in that case it may happen that BufferedInputStream tries hard to look at the source, this code protects against that use.

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

    To reuse it just close it first if it wasn't.

    One limitation though is that if the stream is closed before the whole content of the original stream has been read, then this decorator will have incomplete data, so make sure the whole stream is read before closing.

提交回复
热议问题