tags:

views:

97

answers:

3

Previously we had some zip files within our web application. We would want to pares a specific text document within the zip file. This wasn't a problem:

URL url = getClass().getResource(zipfile);
ZipFile zip = new ZipFile(url.getFile().replaceAll("%20", " "));     
Entry entry = zip.getEntry("file.txt");

InputStream is = zip.getInputStream(entry);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));

String line = reader.readLine();
while (line != null) {
    // do stuff
}

However we've moved these zip files into another module and want to package them within a jar. Unfortunatly creating the ZipFile now fails. I can get an InputStream for the zip: but I have no way of getting an input stream for the entry itself.

InputStream is = getClass().getResourceAsStream(zipfile);
ZipInputStream zis = new ZipInputStream(is);

ZipEntry entry = zis.getNextEntry();
while (entry != null && !entry.getName().equals("file.txt")) {
    entry = zis.getNextEntry();
}

but I have no way of getting an input stream for the entry itself. I tried finding the length of the entry and getting the next n bytes from the ZipInputStream but this didn't work for me. It seemed all bytes read were 0.

Is there a way round this or am I going to have to move the zip files back into the core project?

+2  A: 

entry can give you the inputstream of the inner-zip file.

InputStream innerzipstream = zip.getInputStream(entry);

So you can use

new ZipInputStream(innerzipstream);

and ask the ZipInputStream to retrieve the content of the inner-zip-file (in an ordered fashion, you don't have random access because it's a ZipInputStream)

Look at http://download.oracle.com/javase/1.4.2/docs/api/java/util/zip/ZipInputStream.html

Sequential zip access

As ZipInputStream is reading a zip file from an input stream it has to do things in order:

// DO THIS for each entry
ZipEntry e = zipInputStreamObj.getNextEntry();
e.getName // and all data
int size = e.getSize(); // the byte count
while (size > 0) {
   size -= zipInputStreamObj.read(...);
}
zipInputStreamObj.closeEntry();
// DO THIS END

zipInputStreamObj.close();

Note: I don't know if ZipInputStream.getNextEntry() returns null when end of zip file is reached or not. I hope so because I don't know other way to realize when there are no more entries.

helios
In fact, you can use Class.getResourceAsStream and ZipInputStream to read the outer-zip too. That way, you don't need the ad-hoc file name replace and don't rely on URL for file access.
helios
A: 

I don't know the "proper answer", but you can always copy the resource to a file in a temporary directory.

I would do it using apache commons / io:

    InputStream is;
    OutputStream os;
    try{
        is = this.getClass().getResourceAsStream(zipfile);
        final File tempFile = File.createTempFile("temp", ".zip");
        tempFile.mkdirs();
        os = FileUtils.openOutputStream(tempFile);
        IOUtils.copy(is, os);
    } catch(final IOException e){
        IOUtils.closeQuietly(is);
        IOUtils.closeQuietly(os);
    }
seanizer
+1  A: 

How about TrueZip? Using it you could simply open the zipped file as if it was located inside a directory.

new FileOutputStream("/path/to/some-jar.jar/internal/zip/file.zip/myfile.txt");

According to the docs, infinite nesting is also supported. I have yet to actually use this project, but it's been on my radar for a while and it seems applicable to your problem.

Project Site: https://truezip.dev.java.net/

Andy