10

I can go through ZipInputStream, but before starting the iteration I want to get a specific file that I need during the iteration. How can I do that?

ZipInputStream zin = new ZipInputStream(myInputStream)
while ((entry = zin.getNextEntry()) != null)
 {
    println entry.getName()
}
Jils
  • 783
  • 5
  • 12
  • 32
  • 2
    I don't understand... Iterate the entries till you get the one you want, then process it? – tim_yates Apr 08 '15 at 13:02
  • 1
    First iterate to the file and store it the way you want. Then just iterate again. – Bubletan Apr 08 '15 at 13:07
  • There is also ZipFile (java < 7) and the Zip Filesystem starting from Java7 (although not possible from a ZipInputStream :)), that's why this is not an answer to the question – GPI Apr 08 '15 at 13:07
  • tim_yates: the file in question is necessary for processing the other files. So if the first iteration the file is not what I want, I could no longer process it. – Jils Apr 08 '15 at 13:22

4 Answers4

8

If the myInputStream you're working with comes from a real file on disk then you can simply use java.util.zip.ZipFile instead, which is backed by a RandomAccessFile and provides direct access to the zip entries by name. But if all you have is an InputStream (e.g. if you're processing the stream directly on receipt from a network socket or similar) then you'll have to do your own buffering.

You could copy the stream to a temporary file, then open that file using ZipFile, or if you know the maximum size of the data in advance (e.g. for an HTTP request that declares its Content-Length up front) you could use a BufferedInputStream to buffer it in memory until you've found the required entry.

BufferedInputStream bufIn = new BufferedInputStream(myInputStream);
bufIn.mark(contentLength);
ZipInputStream zipIn = new ZipInputStream(bufIn);
boolean foundSpecial = false;
while ((entry = zin.getNextEntry()) != null) {
  if("special.txt".equals(entry.getName())) {
    // do whatever you need with the special entry
    foundSpecial = true;
    break;
  }
}

if(foundSpecial) {
  // rewind
  bufIn.reset();
  zipIn = new ZipInputStream(bufIn);
  // ....
}

(I haven't tested this code myself, you may find it's necessary to use something like the commons-io CloseShieldInputStream in between the bufIn and the first zipIn, to allow the first zip stream to close without closing the underlying bufIn before you've rewound it).

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
5

use the getName() method on ZipEntry to get the file you want.

ZipInputStream zin = new ZipInputStream(myInputStream)
String myFile = "foo.txt";
while ((entry = zin.getNextEntry()) != null)
{
    if (entry.getName().equals(myFileName)) {
        // process your file
        // stop looking for your file - you've already found it
        break;
    }
}

From Java 7 onwards, you are better off using ZipFile instead of ZipStream if you only want one file and you have a file to read from:

ZipFile zfile = new ZipFile(aFile);
String myFile = "foo.txt";
ZipEntry entry = zfile.getEntry(myFile);
if (entry) {
     // process your file           
}
Robert Christie
  • 20,177
  • 8
  • 42
  • 37
  • For your first code: see my response to tim_yates. For your seconde code: I thought there was something similar to ZipFile. So for my case should use ZipFile. – Jils Apr 08 '15 at 13:39
  • 1
    but HOW to process the file? What you show is the trivial part: getting the entry, but how to get the data of that specific entry? – Martin Mucha Mar 22 '22 at 08:14
2

Look at Finding a file in zip entry

ZipFile file = new ZipFile("file.zip");
ZipInputStream zis = searchImage("foo.png", file);

public searchImage(String name, ZipFile file)
{
  for (ZipEntry e : file.entries){
    if (e.getName().endsWith(name)){
      return file.getInputStream(e);
    }
  }

  return null;
}
Community
  • 1
  • 1
RSCh
  • 171
  • 6
1

I'm late to the party, but all above "answers" does not answer the question and accepted "answer" suggest create temp file which is inefficient.

Lets create sample zip file:

seq 10000 | sed "s/^.*$/a/"> /tmp/a
seq 10000 20000 | sed "s/^.*$/b/"> /tmp/b
seq 20000 30000 | sed "s/^.*$/c/"> /tmp/c
zip /tmp/out.zip /tmp/a /tmp/b /tmp/c

so now we have /tmp/out.zip file, which contains 3 files, each of them full of chars a, b or c.

Now lets read it:

  public static void main(String[] args) throws IOException {
        ZipInputStream zipStream = new ZipInputStream(new FileInputStream("/tmp/out.zip"));
            ZipEntry zipEntry;
            while ((zipEntry = zipStream.getNextEntry()) != null) {
                String name = zipEntry.getName();
                System.out.println("Entry: "+name);
                if (name.equals("tmp/c")) {
                    byte[] bytes = zipStream.readAllBytes();
                    String s = new String(bytes);
                    System.out.println(s);
                }
            }
    }

method readAllBytes seems weird, while we're in processing of stream, but it seems to work, I tested it also on some images, where there is higher chance of failure. So it's probably just unintuitive api, but it seems to work.

Martin Mucha
  • 2,385
  • 1
  • 29
  • 49