Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8080092

ZipInputStream cannot get uncompressed length from a zip file.

XMLWordPrintable

      FULL PRODUCT VERSION :
      >java -version
      java version "1.8.0_40"
      Java(TM) SE Runtime Environment (build 1.8.0_40-b25)
      Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      >ver

      Microsoft Windows [Version 6.1.7601]

      A DESCRIPTION OF THE PROBLEM :
      Using java.util.zip to create zip files and then later reread them, the scanned ZipEntry's show uncompressed size of -1.

      But although it cannot read its own sizes:
        - other zip processers can correctly read the sizes from those same files (tested with Info-zip unzip, and Python zipfile library).
        - java.util.zip does show valid sizes when scanning zip files created by an external zipper (Info-zip zip)

      The only case that fails is java.util.zip reading zip files created by itself.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Commands shown are Windows 7 command prompt

      Copy the enclosed Java source file (ZipArchive.java) into a directory.
      Also in that directory create a subdirectory "root" and create a file in
      it so we have something to zip. (the file I used happened to be
      TestAbbrevValue.java, which you will see in the outputs below.)

      Run the following commands in the directory containing ZipArchive.java.

      Compile the java program:
        del *.class & javac *.java

      Create a zip file with the resulting Java tool:
        java ZipArchive zip java.zip root

      List the java-created zip file's entries using the Java tool (FAILS):
        java ZipArchiver list java.zip
      LIST
        Length Time Name
      ---------- ---------------------------- ----
              -1 Tue Sep 01 18:48:26 PDT 2009 TestAbbrevValue.java

      List the java-created zip file's entries using unzip (succeeds):
        unzip -l java.zip
      Archive: java.zip
        Length Date Time Name
      --------- ---------- ----- ----
            419 09/01/2009 18:48 TestAbbrevValue.java
      --------- -------
            419 1 file

      Create a reference zip file using Info-zip zip:
        del zip.zip 2>nul & cd root & zip -9 -r ../zip.zip . & cd ..

      List the zip-created zip file's entries using the Java tool (succeeds):
        java ZipArchiver list zip.zip
      LIST
        Length Time Name
      ---------- ---------------------------- ----
             419 Tue Sep 01 18:48:26 PDT 2009 TestAbbrevValue.java

      List the zip-created zip file's entries using unzip (succeeds):
        unzip -l zip.zip
      Archive: zip.zip
        Length Date Time Name
      --------- ---------- ----- ----
            419 09/01/2009 18:48 TestAbbrevValue.java
      --------- -------
            419 1 file


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The command

        java ZipArchiver list java.zip

      from the steps to reproduce should yield a correct size.
      ACTUAL -
      Size yielded is -1.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.io.*;
      import java.nio.file.*;
      import java.nio.file.attribute.*;
      import java.util.*;
      import java.util.zip.*;

      /** Uses java.util.zip. */
      class ZipArchiver {

          private static final int BUFFER_SIZE = 4096;

          /** Extracts from archive "archivePath" to directory "rootPath". */
          public void extract(Path archivePath, Path rootPath) throws IOException {
              ZipInputStream zipIn = null;
              try {
                  zipIn = new ZipInputStream(new BufferedInputStream(
                          Files.newInputStream(archivePath)));
                  ZipEntry entry;
                  while ((entry = zipIn.getNextEntry()) != null) {
                      try {
                          String fileName = entry.getName();
                          Path targetFile = rootPath.resolve(fileName);
                          long time = entry.getTime();
                          if (fileName.endsWith("/")) {
                              Files.createDirectories(targetFile);
                          } else {

                              // Extract file.
                              Files.createDirectories(targetFile.getParent());
                              try (OutputStream out =
                                      Files.newOutputStream(targetFile)) {
                                  byte[] buffer = new byte[BUFFER_SIZE];
                                  int bytesRead;
                                  while ((bytesRead = zipIn.read(buffer)) >= 0) {
                                      out.write(buffer, 0, bytesRead);
                                  }
                              } catch (IOException ex) {
                                  System.out.println("*** IO ERROR" +
                                          " EXTRACTING BINARY FILE: " +
                                          ex.getClass().getName());
                                  System.out.println(" " + ex.getMessage());
                              }
                              Files.setLastModifiedTime(targetFile,
                                      FileTime.fromMillis(time));
                          }
                      } catch (Exception ex) {
                          System.out.println("Could not extract file " +
                                  entry.getName() + ": " + ex);
                      } finally {
                          zipIn.closeEntry();
                      }
                  }
              } catch (NoSuchFileException e) {
                  // No ZIP file, no files extracted, not an error.
              } finally {
                  if (zipIn != null)
                      zipIn.close();
              }
          }

          /** Writes a new archive "archivePath" from the contents of
              directory "rootPath". */
          public void write(Path archivePath, Path rootPath) throws IOException {
              try (ZipOutputStream zipOut = new ZipOutputStream(
                      Files.newOutputStream(archivePath))) {
                  zipOut.setLevel(9);
                  Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
                      @Override
                      public FileVisitResult visitFile(Path file,
                              BasicFileAttributes attrs) throws IOException {
                          Path relFile = rootPath.relativize(file);
                          String fileName = file.toString();

                          // Convert name to Zip format.
                          String zipName = relFile.toString().replace(
                                  File.separatorChar, '/');
                          if (zipName.startsWith("./"))
                              zipName = zipName.substring(2);
                          ZipEntry entry = new ZipEntry(zipName);
                          entry.setTime(Files.getLastModifiedTime(file).toMillis());
                          //entry.setSize(Files.size(file)); // not necessary
                          zipOut.putNextEntry(entry);

                          // Store file.
                          InputStream in = Files.newInputStream(file);
                          byte[] buffer = new byte[BUFFER_SIZE];
                          int bytesRead;
                          while ((bytesRead = in.read(buffer)) >= 0) {
                              zipOut.write(buffer, 0, bytesRead);
                          }
                          in.close();
                          zipOut.flush();
                          zipOut.closeEntry();
                          return FileVisitResult.CONTINUE;
                      }
                  });
              }
          }

          /** Lists entries from archive "archivePath". */
          public List<Entry> entries(Path archivePath) throws IOException {
              List<Entry> entries = new ArrayList<>();
              ZipInputStream zipIn = null;
              try {
                  zipIn = new ZipInputStream(new BufferedInputStream(
                          Files.newInputStream(archivePath)));
                  ZipEntry entry;
                  while ((entry = zipIn.getNextEntry()) != null) {
                      try {
                          entries.add(new Entry(entry.getName(),
                                  entry.getSize(), entry.getTime()));
                      } catch (Exception ex) {
                          System.out.println("Error getting zip entry " + ex);
                      } finally {
                          zipIn.closeEntry();
                      }
                      zipIn.closeEntry();
                  }
              } catch (NoSuchFileException e) {
                  // No ZIP file, no files extracted, not an error.
              } finally {
                  if (zipIn != null)
                      zipIn.close();
              }
              return entries;
          }

          public static class Entry {

              public final String name;
              public final long size;
              public final long mtime;

              public Entry(String name, long size, long mtime) {
                  this.name = name;
                  this.size = size;
                  this.mtime = mtime;
              }

              @Override
              public String toString() {
                  return "Entry(name: " + name +
                          ", size: " + size +
                          ", mtime: " + new Date(mtime) +
                          ")";
              }

          }

          /** Tool to exercise functionality. */
          public static void main(String[] args) throws IOException {
              int argsLen = args.length;
              if (argsLen < 1)
                  usage();
              ZipArchiver archiver = new ZipArchiver();
              switch (args[0]) {
              case "zip":
                  if (argsLen != 3)
                      usage();
                  System.out.println("ZIP");
                  archiver.write(Paths.get(args[1]), Paths.get(args[2]));
                  break;
              case "unzip":
                  if (argsLen != 3)
                      usage();
                  System.out.println("UNZIP");
                  archiver.extract(Paths.get(args[1]), Paths.get(args[2]));
                  break;
              case "list":
                  if (argsLen != 2)
                      usage();
                  System.out.println("LIST");
                  System.out.println(" Length Time Name");
                  System.out.println("---------- ---------------------------- ----");
                  for (Entry entry : archiver.entries(Paths.get(args[1]))) {
                      System.out.printf("%10s %s %s%n",
                              entry.size, new Date(entry.mtime), entry.name);
                  }
                  break;
              default:
                  System.err.println("Unknown command: " + args[0]);
                  System.exit(2);
                  break;
              }
          }

          private static void usage() {
              System.err.println("Args: zip|unzip zip-file dir");
              System.err.println(" list zip-file");
              System.exit(2);
          }

      }
      ---------- END SOURCE ----------

            sherman Xueming Shen
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: