How to read multiple times from inpust stream?

Viewed 31

I have InputStream as input (it's a big .zip) which contains several files like:

  • xxx1.xml
  • xxx2.xml
  • xxx2_old.xml

First I need to determine a file I want to process (Lexicographic order) like:

String getFileName(List<String> filenames){
        return filenames.stream()
                .filter(PREDICATE)
                .max(Comparator.naturalOrder());
    }
}

Then I need to pass this .xml file as InputStream for further parsing.

It would be easy to operate on objects in memory, but I don't know how to approach this with InputStream. The solution should be memory efficient so I cannot just save everything. Should I read it 2 times?

2 Answers

Answer depends on target.

DOM parser load all information into memory. SAX parser is a stream processor.

If you're using ZipFile you only need to open the ZIP file once. ZipFile let's you get an input stream based on an entry.

for (Enumeration<? extends ZipEntry> entries = zipFile.entries(); entries.hasMoreElements(); ) {
    ZipEntry entry = entry.nextElement();
    if (entry matches filter) {
        InputStream inputStream = zipFile.getInputStream(entry);
        // use inputStream as needed
    }
}

An alternative is to use ZipInputStream:

try (ZipInputStream zipStream = new ZipInputStream(...)) {
    ZipEntry entry;
    while ((entry = zipStream.getNextEntry()) != null) {
        if (entry matches filter) {
            // use zipStream as needed, just don't close it!
        }
        zipStream.closeEntry();
    }
}

That remark about not closing the ZipInputStream is important; apart from the close() method the stream works just like a stream for a separate entry. Closing it will close the entire ZIP file though.
If needed, Apache Commons IO has CloseShieldInputStream that can be used to wrap the ZIP stream, so when the wrapper is closed the ZIP stream isn't.

Edit: the ZipInputStream solution indeed needs two loops, because the file to use depends on all other files. The ZipFile solution can still be used. Loop once to get the right file, then use zipFile.getInputStream(getEntry(fileName)), or if you store the entry instead of just its name, zipFile.getInputStream(file).

Related