FileDocCategorySizeDatePackage
ZipFile.javaAPI DocAndroid 1.5 API14273Wed May 06 22:41:02 BST 2009java.util.zip

ZipFile

public class ZipFile extends Object implements ZipConstants
This class provides random read access to a ZIP-archive file.

While {@code ZipInputStream} provides stream based read access to a ZIP-archive, this class implements more efficient (file based) access and makes use of the central directory within a ZIP-archive.

Use {@code ZipOutputStream} if you want to create an archive.

A temporary ZIP file can be marked for automatic deletion upon closing it.

see
ZipEntry
see
ZipOutputStream
since
Android 1.0

Fields Summary
String
fileName
File
fileToDeleteOnClose
public static final int
OPEN_READ
Open zip file for read.
public static final int
OPEN_DELETE
Delete zip file when closed.
private RandomAccessFile
mRaf
ZipEntry.LittleEndianReader
ler
private ArrayList
mEntryList
private HashMap
mFastLookup
Constructors Summary
public ZipFile(File file)
Constructs a new {@code ZipFile} with the specified file.

param
file the file to read from.
throws
ZipException if a ZIP error occurs.
throws
IOException if an {@code IOException} occurs.
since
Android 1.0


                                                                              
          
        this(file, OPEN_READ);
    
public ZipFile(File file, int mode)
Opens a file as ZIP-archive. "mode" must be {@code OPEN_READ} or {@code OPEN_DELETE} . The latter sets the "delete on exit" flag through a file.

param
file the ZIP file to read.
param
mode the mode of the file open operation.
throws
IOException if an {@code IOException} occurs.
since
Android 1.0

        if (mode == (OPEN_READ | OPEN_DELETE))
            fileToDeleteOnClose = file; // file.deleteOnExit();
        else if (mode != OPEN_READ)
            throw new IllegalArgumentException("invalid mode");

        fileName = file.getPath();
        mRaf = new RandomAccessFile(fileName, "r");

        mEntryList = new ArrayList<ZipEntry>();

        readCentralDir();

        /*
         * No LinkedHashMap yet, so optimize lookup-by-name by creating
         * a parallel data structure.
         */
        mFastLookup = new HashMap<String, ZipEntry>(mEntryList.size() * 2);
        for (int i = 0; i < mEntryList.size(); i++) {
            ZipEntry entry = mEntryList.get(i);

            mFastLookup.put(entry.getName(), entry);
        }
    
public ZipFile(String name)
Opens a ZIP archived file.

param
name the name of the ZIP file.
throws
IOException if an IOException occurs.
since
Android 1.0

        this(new File(name), OPEN_READ);
    
Methods Summary
public voidclose()
Closes this ZIP file.

throws
IOException if an IOException occurs.
since
Android 1.0

        RandomAccessFile raf = mRaf;

        if (raf != null) { // Only close initialized instances
            synchronized(raf) {
                mRaf = null;
                raf.close();
            }
            if (fileToDeleteOnClose != null) {
                AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        new File(fileName).delete();
                        return null;
                    }
                });
                // fileToDeleteOnClose.delete();
                fileToDeleteOnClose = null;
            }
        }
    
public java.util.Enumerationentries()
Returns an enumeration of the entries. The entries are listed in the order in which they appear in the ZIP archive.

return
the enumeration of the entries.
since
Android 1.0

        return new Enumeration<ZipEntry>() {
            private int i = 0;

            public boolean hasMoreElements() {
                if (mRaf == null) throw new IllegalStateException("Zip File closed.");
                return i < mEntryList.size();
            }

            public ZipEntry nextElement() {
                if (mRaf == null) throw new IllegalStateException("Zip File closed.");
                if (i >= mEntryList.size())
                    throw new NoSuchElementException();
                return (ZipEntry) mEntryList.get(i++);
            }
        };
    
protected voidfinalize()

        close();
    
public java.util.zip.ZipEntrygetEntry(java.lang.String entryName)
Gets the ZIP entry with the specified name from this {@code ZipFile}.

param
entryName the name of the entry in the ZIP file.
return
a {@code ZipEntry} or {@code null} if the entry name does not exist in the ZIP file.
since
Android 1.0

        if (entryName != null) {
            ZipEntry ze = mFastLookup.get(entryName);
            if (ze == null) ze = mFastLookup.get(entryName + "/");
            return ze;
        }
        throw new NullPointerException();
    
public java.io.InputStreamgetInputStream(java.util.zip.ZipEntry entry)
Returns an input stream on the data of the specified {@code ZipEntry}.

param
entry the ZipEntry.
return
an input stream of the data contained in the {@code ZipEntry}.
throws
IOException if an {@code IOException} occurs.
since
Android 1.0

        /*
         * Make sure this ZipEntry is in this Zip file.  We run it through
         * the name lookup.
         */
        entry = getEntry(entry.getName());
        if (entry == null)
            return null;

        /*
         * Create a ZipInputStream at the right part of the file.
         */
        RandomAccessFile raf = mRaf;
        if (raf != null) {
            synchronized (raf) {
                // Unfortunately we don't know the entry data's start position.
                // All we have is the position of the entry's local header.
                // At position 28 we find the length of the extra data.
                // In some cases this length differs from the one coming in
                // the central header!!!
                RAFStream rafstrm = new RAFStream(raf, entry.mLocalHeaderRelOffset + 28);
                int localExtraLenOrWhatever = ler.readShortLE(rafstrm);
                // Now we need to skip the name
                // and this "extra" data or whatever it is:
                rafstrm.skip(entry.nameLen + localExtraLenOrWhatever);
                rafstrm.mLength = rafstrm.mOffset + entry.compressedSize;
                if (entry.compressionMethod == ZipEntry.DEFLATED) {
                    return new InflaterInputStream(rafstrm, new Inflater(true));
                } else {
                    return rafstrm;
                }
            }
        }
        throw new IllegalStateException("Zip File closed");
    
public java.lang.StringgetName()
Gets the file name of this {@code ZipFile}.

return
the file name of this {@code ZipFile}.
since
Android 1.0

        return fileName;
    
private voidreadCentralDir()

        long scanOffset, stopOffset;
        long sig;

        /*
         * Scan back, looking for the End Of Central Directory field.  If
         * the archive doesn't have a comment, we'll hit it on the first
         * try.
         *
         * No need to synchronize mRaf here -- we only do this when we
         * first open the Zip file.
         */
        scanOffset = mRaf.length() - ENDHDR;
        if (scanOffset < 0)
            throw new ZipException("too short to be Zip");

        stopOffset = scanOffset - 65536;
        if (stopOffset < 0)
            stopOffset = 0;

        while (true) {
            mRaf.seek(scanOffset);
            if (ZipEntry.readIntLE(mRaf) == 101010256L)
                break;

            //System.out.println("not found at " + scanOffset);
            scanOffset--;
            if (scanOffset < stopOffset)
                throw new ZipException("EOCD not found; not a Zip archive?");
        }

        /*
         * Found it, read the EOCD.
         *
         * For performance we want to use buffered I/O when reading the
         * file.  We wrap a buffered stream around the random-access file
         * object.  If we just read from the RandomAccessFile we'll be
         * doing a read() system call every time.
         */
        RAFStream rafs = new RAFStream(mRaf, mRaf.getFilePointer());
        BufferedInputStream bin = new BufferedInputStream(rafs, ENDHDR);
        int diskNumber, diskWithCentralDir, numEntries, totalNumEntries;
        //long centralDirSize;
        long centralDirOffset;
        //int commentLen;

        diskNumber = ler.readShortLE(bin);
        diskWithCentralDir = ler.readShortLE(bin);
        numEntries = ler.readShortLE(bin);
        totalNumEntries = ler.readShortLE(bin);
        /*centralDirSize =*/ ler.readIntLE(bin);
        centralDirOffset = ler.readIntLE(bin);
        /*commentLen =*/ ler.readShortLE(bin);

        if (numEntries != totalNumEntries ||
            diskNumber != 0 ||
            diskWithCentralDir != 0)
            throw new ZipException("spanned archives not supported");

        /*
         * Seek to the first CDE and read all entries.
         */
        rafs = new RAFStream(mRaf, centralDirOffset);
        bin = new BufferedInputStream(rafs, 4096);
        for (int i = 0; i < numEntries; i++) {
            ZipEntry newEntry;

            newEntry = new ZipEntry(ler, bin);
            mEntryList.add(newEntry);
        }
    
public intsize()
Returns the number of {@code ZipEntries} in this {@code ZipFile}.

return
the number of entries in this file.
since
Android 1.0

        return mEntryList.size();