< prev index next >

src/java.base/share/classes/java/util/zip/ZipFile.java

Print this page

        

@@ -23,10 +23,11 @@
  * questions.
  */
 
 package java.util.zip;
 
+import java.io.ByteArrayInputStream;
 import java.io.Closeable;
 import java.io.InputStream;
 import java.io.IOException;
 import java.io.EOFException;
 import java.io.File;

@@ -65,10 +66,13 @@
     private volatile boolean closeRequested = false;
 
     private static final int STORED = ZipEntry.STORED;
     private static final int DEFLATED = ZipEntry.DEFLATED;
 
+    // Max buffer size when returning bytebuffers directly
+    private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
+
     /**
      * Mode flag to open a zip file for reading.
      */
     public static final int OPEN_READ = 0x1;
 

@@ -343,33 +347,51 @@
      */
     public InputStream getInputStream(ZipEntry entry) throws IOException {
         if (entry == null) {
             throw new NullPointerException("entry");
         }
-        long jzentry = 0;
-        ZipFileInputStream in = null;
+
+        long jzentry, csize, size;
+        int cmethod;
         synchronized (this) {
             ensureOpen();
             if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
                 jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false);
             } else {
                 jzentry = getEntry(jzfile, zc.getBytes(entry.name), false);
             }
             if (jzentry == 0) {
                 return null;
             }
-            in = new ZipFileInputStream(jzentry);
+            size = getEntrySize(jzentry);
+            csize = getEntryCSize(jzentry);
+            cmethod = getEntryMethod(jzentry);
+        }
 
-            switch (getEntryMethod(jzentry)) {
+        if (csize >= 0 && size > 0 && size < 128 * 1024) {
+            try {
+                return new ByteArrayInputStream(getBytes(jzentry, csize, size, cmethod));
+            } finally {
+                synchronized (this) {
+                    if (jzfile != 0) {
+                        freeEntry(jzfile, jzentry);
+                    }
+                }
+            }
+        }
+
+        ZipFileInputStream in = new ZipFileInputStream(jzentry, csize, size);
+
+        switch (cmethod) {
             case STORED:
                 synchronized (streams) {
                     streams.put(in, null);
                 }
                 return in;
             case DEFLATED:
                 // MORE: Compute good size for inflater stream:
-                long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack
+            size += 2; // Inflater likes a bit of slack
                 if (size > 65536) size = 8192;
                 if (size <= 0) size = 4096;
                 Inflater inf = getInflater();
                 InputStream is =
                     new ZipFileInflaterInputStream(in, inf, (int)size);

@@ -379,11 +401,10 @@
                 return is;
             default:
                 throw new ZipException("invalid compression method");
             }
         }
-    }
 
     private class ZipFileInflaterInputStream extends InflaterInputStream {
         private volatile boolean closeRequested = false;
         private boolean eof = false;
         private final ZipFileInputStream zfin;

@@ -467,10 +488,107 @@
     }
 
     // List of available Inflater objects for decompression
     private Deque<Inflater> inflaterCache = new ArrayDeque<>();
 
+    /*
+     * Uncompress the zip entry into a new byte[].
+     *
+     * This method can only read entries smaller than 2GB, for larger entries
+     * use getInputStream.
+     *
+     * @param entry the zip file entry
+     *
+     * @return the byte[] with the deflated contents of the specified zip file entry.
+     *
+     * @throws ZipException if a ZIP format error has occurred
+     * @throws IOException if an I/O error has occurred
+     * @throws IllegalStateException if the zip file has been closed
+     * @throws OutOfMemory if the zip entry is larger than 2GB
+     */
+    private byte[] getBytes(ZipEntry entry) throws IOException {
+        if (entry == null) {
+            throw new NullPointerException("entry");
+        }
+
+        long jzentry, csize, size;
+        int cmethod;
+        synchronized (this) {
+            ensureOpen();
+            if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
+                jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false);
+            } else {
+                jzentry = getEntry(jzfile, zc.getBytes(entry.name), false);
+            }
+            if (jzentry == 0) {
+                return null;
+            }
+
+            csize = getEntryCSize(jzentry);
+            size = getEntrySize(jzentry);
+            cmethod = getEntryMethod(jzentry);
+        }
+
+        try {
+            return getBytes(jzentry, csize, size, cmethod);
+        } finally {
+            synchronized (this) {
+                if (jzfile != 0) {
+                    freeEntry(jzfile, jzentry);
+                }
+            }
+        }
+    }
+
+    private byte[] getBytes(long jzentry, long csize, long size, int cmethod)
+            throws IOException {
+
+        if (csize < 0 || size < 0) {
+            throw new ZipException("Unknown size of ZipEntry");
+        }
+        if (csize > MAX_BUFFER_SIZE || size > MAX_BUFFER_SIZE) {
+            throw new OutOfMemoryError("ZipEntry too large");
+        }
+
+        byte[] cbytes = new byte[(int) csize];
+        readCompressedBytes(jzentry, (int) csize, cbytes);
+        switch (cmethod) {
+            case STORED:
+                return cbytes;
+            case DEFLATED:
+                byte[] bytes = new byte[(int) size];
+                Inflater inf = getInflater();
+                try {
+                    inf.setInput(cbytes);
+                    inf.inflate(bytes);
+                } catch (DataFormatException e) {
+                    String s = e.getMessage();
+                    throw new ZipException(s != null ? s : "Invalid ZLIB data format");
+                } finally {
+                    releaseInflater(inf);
+                }
+                return bytes;
+            default:
+                throw new ZipException("invalid compression method");
+        }
+    }
+
+    private int readCompressedBytes(long jzentry, int csize, byte[] buf)
+            throws IOException {
+        assert csize == buf.length;
+        synchronized (this) {
+            ensureOpenOrZipException();
+            int n = 0;
+            int nread = 0;
+            while (nread < csize
+                   && (n = read(jzfile, jzentry, nread, buf, nread, csize - nread)) > 0) {
+                nread += n;
+            }
+            return nread;
+        }
+    }
+
     /**
      * Returns the path name of the ZIP file.
      * @return the path name of the ZIP file
      */
     public String getName() {

@@ -689,14 +807,14 @@
         protected long jzentry; // address of jzentry data
         private   long pos;     // current position within entry data
         protected long rem;     // number of remaining bytes within entry
         protected long size;    // uncompressed size of this entry
 
-        ZipFileInputStream(long jzentry) {
+        ZipFileInputStream(long jzentry, long csize, long size) {
             pos = 0;
-            rem = getEntryCSize(jzentry);
-            size = getEntrySize(jzentry);
+            rem = csize;
+            this.size = size;
             this.jzentry = jzentry;
         }
 
         public int read(byte b[], int off, int len) throws IOException {
             synchronized (ZipFile.this) {

@@ -777,13 +895,20 @@
     }
 
     static {
         sun.misc.SharedSecrets.setJavaUtilZipFileAccess(
             new sun.misc.JavaUtilZipFileAccess() {
+                @Override
                 public boolean startsWithLocHeader(ZipFile zip) {
                     return zip.startsWithLocHeader();
                 }
+
+                @Override
+                public byte[] getBytes(ZipFile zip, ZipEntry entry)
+                        throws IOException {
+                    return zip.getBytes(entry);
+                }
              }
         );
     }
 
     /**
< prev index next >