All Downloads are FREE. Search and download functionalities are using the official Maven repository.

java.util.zip.ZipInputStream Maven / Gradle / Ivy

There is a newer version: 17.alpha.0.57
Show newest version
/*
 * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package java.util.zip;

import java.io.InputStream;
import java.io.IOException;
import java.io.EOFException;
import java.io.PushbackInputStream;
import java.nio.charset.Charset;

import sun.nio.cs.UTF_8;

import static java.util.zip.ZipConstants64.*;
import static java.util.zip.ZipUtils.*;

/**
 * This class implements an input stream filter for reading files in the
 * ZIP file format. Includes support for both compressed and uncompressed
 * entries.
 *
 * @author      David Connelly
 * @since 1.1
 */
public class ZipInputStream extends InflaterInputStream implements ZipConstants {
    private ZipEntry entry;
    private int flag;
    private CRC32 crc = new CRC32();
    private long remaining;
    private byte[] tmpbuf = new byte[512];

    private static final int STORED = ZipEntry.STORED;
    private static final int DEFLATED = ZipEntry.DEFLATED;

    private boolean closed = false;
    // this flag is set to true after EOF has reached for
    // one entry
    private boolean entryEOF = false;

    private ZipCoder zc;

    /**
     * Check to make sure that this stream has not been closed
     */
    private void ensureOpen() throws IOException {
        if (closed) {
            throw new IOException("Stream closed");
        }
    }

    /**
     * Creates a new ZIP input stream.
     *
     * 

The UTF-8 {@link java.nio.charset.Charset charset} is used to * decode the entry names. * * @param in the actual input stream */ public ZipInputStream(InputStream in) { this(in, UTF_8.INSTANCE); } /** * Creates a new ZIP input stream. * * @param in the actual input stream * * @param charset * The {@linkplain java.nio.charset.Charset charset} to be * used to decode the ZIP entry name (ignored if the * language * encoding bit of the ZIP entry's general purpose bit * flag is set). * * @since 1.7 */ public ZipInputStream(InputStream in, Charset charset) { super(new PushbackInputStream(in, 512), new Inflater(true), 512); usesDefaultInflater = true; if (in == null) { throw new NullPointerException("in is null"); } if (charset == null) throw new NullPointerException("charset is null"); this.zc = ZipCoder.get(charset); } /** * Reads the next ZIP file entry and positions the stream at the * beginning of the entry data. * @return the next ZIP file entry, or null if there are no more entries * @throws ZipException if a ZIP file error has occurred * @throws IOException if an I/O error has occurred */ public ZipEntry getNextEntry() throws IOException { ensureOpen(); if (entry != null) { closeEntry(); } crc.reset(); inf.reset(); if ((entry = readLOC()) == null) { return null; } if (entry.method == STORED) { remaining = entry.size; } entryEOF = false; return entry; } /** * Closes the current ZIP entry and positions the stream for reading the * next entry. * @throws ZipException if a ZIP file error has occurred * @throws IOException if an I/O error has occurred */ public void closeEntry() throws IOException { ensureOpen(); while (read(tmpbuf, 0, tmpbuf.length) != -1) ; entryEOF = true; } /** * Returns 0 after EOF has reached for the current entry data, * otherwise always return 1. *

* Programs should not count on this method to return the actual number * of bytes that could be read without blocking. * * @return 1 before EOF and 0 after EOF has reached for current entry. * @throws IOException if an I/O error occurs. * */ public int available() throws IOException { ensureOpen(); if (entryEOF) { return 0; } else { return 1; } } /** * Reads from the current ZIP entry into an array of bytes. * If {@code len} is not zero, the method * blocks until some input is available; otherwise, no * bytes are read and {@code 0} is returned. * @param b the buffer into which the data is read * @param off the start offset in the destination array {@code b} * @param len the maximum number of bytes read * @return the actual number of bytes read, or -1 if the end of the * entry is reached * @throws NullPointerException if {@code b} is {@code null}. * @throws IndexOutOfBoundsException if {@code off} is negative, * {@code len} is negative, or {@code len} is greater than * {@code b.length - off} * @throws ZipException if a ZIP file error has occurred * @throws IOException if an I/O error has occurred */ public int read(byte[] b, int off, int len) throws IOException { ensureOpen(); if (off < 0 || len < 0 || off > b.length - len) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } if (entry == null) { return -1; } switch (entry.method) { case DEFLATED: len = super.read(b, off, len); if (len == -1) { readEnd(entry); entryEOF = true; entry = null; } else { crc.update(b, off, len); } return len; case STORED: if (remaining <= 0) { entryEOF = true; entry = null; return -1; } if (len > remaining) { len = (int)remaining; } len = in.read(b, off, len); if (len == -1) { throw new ZipException("unexpected EOF"); } crc.update(b, off, len); remaining -= len; if (remaining == 0 && entry.crc != crc.getValue()) { throw new ZipException( "invalid entry CRC (expected 0x" + Long.toHexString(entry.crc) + " but got 0x" + Long.toHexString(crc.getValue()) + ")"); } return len; default: throw new ZipException("invalid compression method"); } } /** * Skips specified number of bytes in the current ZIP entry. * @param n the number of bytes to skip * @return the actual number of bytes skipped * @throws ZipException if a ZIP file error has occurred * @throws IOException if an I/O error has occurred * @throws IllegalArgumentException if {@code n < 0} */ public long skip(long n) throws IOException { if (n < 0) { throw new IllegalArgumentException("negative skip length"); } ensureOpen(); int max = (int)Math.min(n, Integer.MAX_VALUE); int total = 0; while (total < max) { int len = max - total; if (len > tmpbuf.length) { len = tmpbuf.length; } len = read(tmpbuf, 0, len); if (len == -1) { entryEOF = true; break; } total += len; } return total; } /** * Closes this input stream and releases any system resources associated * with the stream. * @throws IOException if an I/O error has occurred */ public void close() throws IOException { if (!closed) { super.close(); closed = true; } } private byte[] b = new byte[256]; /* * Reads local file (LOC) header for next entry. */ private ZipEntry readLOC() throws IOException { try { readFully(tmpbuf, 0, LOCHDR); } catch (EOFException e) { return null; } if (get32(tmpbuf, 0) != LOCSIG) { return null; } // get flag first, we need check USE_UTF8. flag = get16(tmpbuf, LOCFLG); // get the entry name and create the ZipEntry first int len = get16(tmpbuf, LOCNAM); int blen = b.length; if (len > blen) { do { blen = blen * 2; } while (len > blen); b = new byte[blen]; } readFully(b, 0, len); // Force to use UTF-8 if the USE_UTF8 bit is ON ZipEntry e = createZipEntry(((flag & USE_UTF8) != 0) ? ZipCoder.toStringUTF8(b, len) : zc.toString(b, len)); // now get the remaining fields for the entry if ((flag & 1) == 1) { throw new ZipException("encrypted ZIP entry not supported"); } e.method = get16(tmpbuf, LOCHOW); e.xdostime = get32(tmpbuf, LOCTIM); if ((flag & 8) == 8) { /* "Data Descriptor" present */ if (e.method != DEFLATED) { throw new ZipException( "only DEFLATED entries can have EXT descriptor"); } } else { e.crc = get32(tmpbuf, LOCCRC); e.csize = get32(tmpbuf, LOCSIZ); e.size = get32(tmpbuf, LOCLEN); } len = get16(tmpbuf, LOCEXT); if (len > 0) { byte[] extra = new byte[len]; readFully(extra, 0, len); e.setExtra0(extra, e.csize == ZIP64_MAGICVAL || e.size == ZIP64_MAGICVAL, true); } return e; } /** * Creates a new {@code ZipEntry} object for the specified * entry name. * * @param name the ZIP file entry name * @return the ZipEntry just created */ protected ZipEntry createZipEntry(String name) { return new ZipEntry(name); } /** * Reads end of deflated entry as well as EXT descriptor if present. * * Local headers for DEFLATED entries may optionally be followed by a * data descriptor, and that data descriptor may optionally contain a * leading signature (EXTSIG). * * From the zip spec http://www.pkware.com/documents/casestudies/APPNOTE.TXT * * """Although not originally assigned a signature, the value 0x08074b50 * has commonly been adopted as a signature value for the data descriptor * record. Implementers should be aware that ZIP files may be * encountered with or without this signature marking data descriptors * and should account for either case when reading ZIP files to ensure * compatibility.""" */ private void readEnd(ZipEntry e) throws IOException { int n = inf.getRemaining(); if (n > 0) { ((PushbackInputStream)in).unread(buf, len - n, n); } if ((flag & 8) == 8) { /* "Data Descriptor" present */ if (inf.getBytesWritten() > ZIP64_MAGICVAL || inf.getBytesRead() > ZIP64_MAGICVAL) { // ZIP64 format readFully(tmpbuf, 0, ZIP64_EXTHDR); long sig = get32(tmpbuf, 0); if (sig != EXTSIG) { // no EXTSIG present e.crc = sig; e.csize = get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC); e.size = get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC); ((PushbackInputStream)in).unread( tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC, ZIP64_EXTCRC); } else { e.crc = get32(tmpbuf, ZIP64_EXTCRC); e.csize = get64(tmpbuf, ZIP64_EXTSIZ); e.size = get64(tmpbuf, ZIP64_EXTLEN); } } else { readFully(tmpbuf, 0, EXTHDR); long sig = get32(tmpbuf, 0); if (sig != EXTSIG) { // no EXTSIG present e.crc = sig; e.csize = get32(tmpbuf, EXTSIZ - EXTCRC); e.size = get32(tmpbuf, EXTLEN - EXTCRC); ((PushbackInputStream)in).unread( tmpbuf, EXTHDR - EXTCRC, EXTCRC); } else { e.crc = get32(tmpbuf, EXTCRC); e.csize = get32(tmpbuf, EXTSIZ); e.size = get32(tmpbuf, EXTLEN); } } } if (e.size != inf.getBytesWritten()) { throw new ZipException( "invalid entry size (expected " + e.size + " but got " + inf.getBytesWritten() + " bytes)"); } if (e.csize != inf.getBytesRead()) { throw new ZipException( "invalid entry compressed size (expected " + e.csize + " but got " + inf.getBytesRead() + " bytes)"); } if (e.crc != crc.getValue()) { throw new ZipException( "invalid entry CRC (expected 0x" + Long.toHexString(e.crc) + " but got 0x" + Long.toHexString(crc.getValue()) + ")"); } } /* * Reads bytes, blocking until all bytes are read. */ private void readFully(byte[] b, int off, int len) throws IOException { while (len > 0) { int n = in.read(b, off, len); if (n == -1) { throw new EOFException(); } off += n; len -= n; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy