Java压缩包解压到指定文件

时间:2024-04-15 21:34:53

    在获得一个以Zip格式压缩的文件之后,需要将其进行解压缩,还原成压缩前的文件。若是使用Java自带的压缩工具包来实现解压缩文件到指定文件夹的功能,因为jdk提供的zip只能按UTF-8格式处理,Windows系统中文件名是以GBK方式编码的,所以如果是解压一个包含中文文件名的zip,会报非法参数异常,如图所示:

    

所以要实现解压缩,就得对DeflaterOutputStream.javaInflaterInputStream.javaZipConstants.javaZipEntry.javaZipInputStream.java以及ZipOutputStream.java这些相关的类进行修改,过程如下:

  1. 因为从 J2SE 1.4 开始,Java 编译器不再支持 import 进未命包名的类、接口,所以在创建的Java项目中,一定要新建一个自己定义的包,包命名的格式一般为学校域名的逆序+自己的网名,比如cn.edu.xidian.crytoll
  2. 在包内新建DeflaterOutputStream类,代码如下:

DeflaterOutputStream.java:

package cn.edu.xdian.crytoll;

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.Deflater;

/**
 * This class implements an output stream filter for compressing data in
 * the "deflate" compression format. It is also used as the basis for other
 * types of compression filters, such as GZIPOutputStream.
 *
 * @see		Deflater
 * @version 	1.36, 03/13/06
 * @author 	David Connelly
 */
public
class DeflaterOutputStream extends FilterOutputStream {
    /**
     * Compressor for this stream.
     */
    protected Deflater def;

    /**
     * Output buffer for writing compressed data.
     */
    protected byte[] buf;
   
    /**
     * Indicates that the stream has been closed.
     */

    private boolean closed = false;

    /**
     * Creates a new output stream with the specified compressor and
     * buffer size.
     * @param out the output stream
     * @param def the compressor ("deflater")
     * @param size the output buffer size
     * @exception IllegalArgumentException if size is <= 0
     */
    public DeflaterOutputStream(OutputStream out, Deflater def, int size) {
        super(out);
        if (out == null || def == null) {
            throw new NullPointerException();
        } else if (size <= 0) {
            throw new IllegalArgumentException("buffer size <= 0");
        }
        this.def = def;
        buf = new byte[size];
    }

    /**
     * Creates a new output stream with the specified compressor and
     * a default buffer size.
     * @param out the output stream
     * @param def the compressor ("deflater")
     */
    public DeflaterOutputStream(OutputStream out, Deflater def) {
	this(out, def, 512);
    }

    boolean usesDefaultDeflater = false;

    /**
     * Creates a new output stream with a default compressor and buffer size.
     * @param out the output stream
     */
    public DeflaterOutputStream(OutputStream out) {
	this(out, new Deflater());
        usesDefaultDeflater = true;
    }

    /**
     * Writes a byte to the compressed output stream. This method will
     * block until the byte can be written.
     * @param b the byte to be written
     * @exception IOException if an I/O error has occurred
     */
    public void write(int b) throws IOException {
        byte[] buf = new byte[1];
	buf[0] = (byte)(b & 0xff);
	write(buf, 0, 1);
    }

    /**
     * Writes an array of bytes to the compressed output stream. This
     * method will block until all the bytes are written.
     * @param b the data to be written
     * @param off the start offset of the data
     * @param len the length of the data
     * @exception IOException if an I/O error has occurred
     */
    public void write(byte[] b, int off, int len) throws IOException {
	if (def.finished()) {
	    throw new IOException("write beyond end of stream");
	}
        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
	    throw new IndexOutOfBoundsException();
	} else if (len == 0) {
	    return;
	}
	if (!def.finished()) {
            // Deflate no more than stride bytes at a time.  This avoids
            // excess copying in deflateBytes (see Deflater.c)
            int stride = buf.length;
            for (int i = 0; i < len; i+= stride) {
                def.setInput(b, off + i, Math.min(stride, len - i));
                while (!def.needsInput()) {
                    deflate();
                }
            }
	}
    }

    /**
     * Finishes writing compressed data to the output stream without closing
     * the underlying stream. Use this method when applying multiple filters
     * in succession to the same output stream.
     * @exception IOException if an I/O error has occurred
     */
    public void finish() throws IOException {
	if (!def.finished()) {
	    def.finish();
	    while (!def.finished()) {
		deflate();
	    }
	}
    }

    /**
     * Writes remaining compressed data to the output stream and closes the
     * underlying stream.
     * @exception IOException if an I/O error has occurred
     */
    public void close() throws IOException {
        if (!closed) {
            finish();
            if (usesDefaultDeflater)
                def.end();
            out.close();
            closed = true;
        }
    }

    /**
     * Writes next block of compressed data to the output stream.
     * @throws IOException if an I/O error has occurred
     */
    protected void deflate() throws IOException {
	int len = def.deflate(buf, 0, buf.length);
	if (len > 0) {
	    out.write(buf, 0, len);
	}
    }
}

  3. 在包内新建InflaterInputStream类,代码如下:

InflaterInputStream.java:

package cn.edu.xdian.crytoll;


import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import java.util.zip.ZipException;

/**
 * This class implements a stream filter for uncompressing data in the
 * "deflate" compression format. It is also used as the basis for other
 * decompression filters, such as GZIPInputStream.
 *
 * @see		Inflater
 * @version 	1.40, 04/07/06
 * @author 	David Connelly
 */
public
class InflaterInputStream extends FilterInputStream {
    /**
     * Decompressor for this stream.
     */
    protected Inflater inf;

    /**
     * Input buffer for decompression.
     */
    protected byte[] buf;

    /**
     * Length of input buffer.
     */
    protected int len;

    private boolean closed = false;
    // this flag is set to true after EOF has reached
    private boolean reachEOF = false;
    
    /**
     * 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 input stream with the specified decompressor and
     * buffer size.
     * @param in the input stream
     * @param inf the decompressor ("inflater")
     * @param size the input buffer size
     * @exception IllegalArgumentException if size is <= 0
     */
    public InflaterInputStream(InputStream in, Inflater inf, int size) {
	super(in);
        if (in == null || inf == null) {
            throw new NullPointerException();
        } else if (size <= 0) {
            throw new IllegalArgumentException("buffer size <= 0");
        }
	this.inf = inf;
	buf = new byte[size];
    }

    /**
     * Creates a new input stream with the specified decompressor and a
     * default buffer size.
     * @param in the input stream
     * @param inf the decompressor ("inflater")
     */
    public InflaterInputStream(InputStream in, Inflater inf) {
	this(in, inf, 512);
    }

    boolean usesDefaultInflater = false;

    /**
     * Creates a new input stream with a default decompressor and buffer size.
     * @param in the input stream
     */
    public InflaterInputStream(InputStream in) {
	this(in, new Inflater());
        usesDefaultInflater = true;
    }

    private byte[] singleByteBuf = new byte[1];

    /**
     * Reads a byte of uncompressed data. This method will block until
     * enough input is available for decompression.
     * @return the byte read, or -1 if end of compressed input is reached
     * @exception IOException if an I/O error has occurred
     */
    public int read() throws IOException {
	ensureOpen();
	return read(singleByteBuf, 0, 1) == -1 ? -1 : singleByteBuf[0] & 0xff;
    }

    /**
     * Reads uncompressed data into an array of bytes. If <code>len</code> is not
     * zero, the method will block until some input can be decompressed; otherwise,
     * no bytes are read and <code>0</code> is returned.
     * @param b the buffer into which the data is read
     * @param off the start offset in the destination array <code>b</code>
     * @param len the maximum number of bytes read
     * @return the actual number of bytes read, or -1 if the end of the
     *         compressed input is reached or a preset dictionary is needed
     * @exception  NullPointerException If <code>b</code> is <code>null</code>.
     * @exception  IndexOutOfBoundsException If <code>off</code> is negative, 
     * <code>len</code> is negative, or <code>len</code> is greater than 
     * <code>b.length - off</code>
     * @exception ZipException if a ZIP format error has occurred
     * @exception IOException if an I/O error has occurred
     */
    public int read(byte[] b, int off, int len) throws IOException {
	ensureOpen();
	if (b == null) {
	    throw new NullPointerException();
	} else if (off < 0 || len < 0 || len > b.length - off) {
	    throw new IndexOutOfBoundsException();
	} else if (len == 0) {
	    return 0;
	}
	try {
	    int n;
	    while ((n = inf.inflate(b, off, len)) == 0) {
		if (inf.finished() || inf.needsDictionary()) {
                    reachEOF = true;
		    return -1;
		}
		if (inf.needsInput()) {
		    fill();
		}
	    }
	    return n;
	} catch (DataFormatException e) {
	    String s = e.getMessage();
	    throw new ZipException(s != null ? s : "Invalid ZLIB data format");
	}
    }

    /**
     * Returns 0 after EOF has been reached, otherwise always return 1.
     * <p>
     * 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.
     * @exception  IOException  if an I/O error occurs.
     * 
     */
    public int available() throws IOException {
        ensureOpen();
        if (reachEOF) {
            return 0;
        } else {
            return 1;
        }
    }

    private byte[] b = new byte[512];

    /**
     * Skips specified number of bytes of uncompressed data.
     * @param n the number of bytes to skip
     * @return the actual number of bytes skipped.
     * @exception IOException if an I/O error has occurred
     * @exception IllegalArgumentException if 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 > b.length) {
		len = b.length;
	    }
	    len = read(b, 0, len);
	    if (len == -1) {
                reachEOF = true;
		break;
	    }
	    total += len;
	}
	return total;
    }

    /**
     * Closes this input stream and releases any system resources associated
     * with the stream.
     * @exception IOException if an I/O error has occurred
     */
    public void close() throws IOException {
        if (!closed) {
            if (usesDefaultInflater)
                inf.end();
	    in.close();
            closed = true;
        }
    }

    /**
     * Fills input buffer with more data to decompress.
     * @exception IOException if an I/O error has occurred
     */
    protected void fill() throws IOException {
	ensureOpen();
	len = in.read(buf, 0, buf.length);
	if (len == -1) {
	    throw new EOFException("Unexpected end of ZLIB input stream");
	}
	inf.setInput(buf, 0, len);
    }

    /**
     * Tests if this input stream supports the <code>mark</code> and
     * <code>reset</code> methods. The <code>markSupported</code>
     * method of <code>InflaterInputStream</code> returns
     * <code>false</code>.
     *
     * @return  a <code>boolean</code> indicating if this stream type supports
     *          the <code>mark</code> and <code>reset</code> methods.
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.InputStream#reset()
     */
    public boolean markSupported() {
        return false;
    }
 
    /**
     * Marks the current position in this input stream.
     *
     * <p> The <code>mark</code> method of <code>InflaterInputStream</code>
     * does nothing.
     *
     * @param   readlimit   the maximum limit of bytes that can be read before
     *                      the mark position becomes invalid.
     * @see     java.io.InputStream#reset()
     */
    public synchronized void mark(int readlimit) {
    }
 
    /**
     * Repositions this stream to the position at the time the
     * <code>mark</code> method was last called on this input stream.
     *
     * <p> The method <code>reset</code> for class
     * <code>InflaterInputStream</code> does nothing except throw an
     * <code>IOException</code>.
     *
     * @exception  IOException  if this method is invoked.
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.IOException
     */
    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }
}

4. 在包中新建ZipConstants接口,代码如下:

ZipConstants.java:

package cn.edu.xdian.crytoll;
interface ZipConstants {
    /*
     * Header signatures
     */
    static long LOCSIG = 0x04034b50L;	// "PK\003\004"
    static long EXTSIG = 0x08074b50L;	// "PK\007\008"
    static long CENSIG = 0x02014b50L;	// "PK\001\002"
    static long ENDSIG = 0x06054b50L;	// "PK\005\006"

    /*
     * Header sizes in bytes (including signatures)
     */
    static final int LOCHDR = 30;	// LOC header size
    static final int EXTHDR = 16;	// EXT header size
    static final int CENHDR = 46;	// CEN header size
    static final int ENDHDR = 22;	// END header size

    /*
     * Local file (LOC) header field offsets
     */
    static final int LOCVER = 4;	// version needed to extract
    static final int LOCFLG = 6;	// general purpose bit flag
    static final int LOCHOW = 8;	// compression method
    static final int LOCTIM = 10;	// modification time
    static final int LOCCRC = 14;	// uncompressed file crc-32 value
    static final int LOCSIZ = 18;	// compressed size
    static final int LOCLEN = 22;	// uncompressed size
    static final int LOCNAM = 26;	// filename length
    static final int LOCEXT = 28;	// extra field length

    /*
     * Extra local (EXT) header field offsets
     */
    static final int EXTCRC = 4;	// uncompressed file crc-32 value
    static final int EXTSIZ = 8;	// compressed size
    static final int EXTLEN = 12;	// uncompressed size

    /*
     * Central directory (CEN) header field offsets
     */
    static final int CENVEM = 4;	// version made by
    static final int CENVER = 6;	// version needed to extract
    static final int CENFLG = 8;	// encrypt, decrypt flags
    static final int CENHOW = 10;	// compression method
    static final int CENTIM = 12;	// modification time
    static final int CENCRC = 16;	// uncompressed file crc-32 value
    static final int CENSIZ = 20;	// compressed size
    static final int CENLEN = 24;	// uncompressed size
    static final int CENNAM = 28;	// filename length
    static final int CENEXT = 30;	// extra field length
    static final int CENCOM = 32;	// comment length
    static final int CENDSK = 34;	// disk number start
    static final int CENATT = 36;	// internal file attributes
    static final int CENATX = 38;	// external file attributes
    static final int CENOFF = 42;	// LOC header offset

    /*
     * End of central directory (END) header field offsets
     */
    static final int ENDSUB = 8;	// number of entries on this disk
    static final int ENDTOT = 10;	// total number of entries
    static final int ENDSIZ = 12;	// central directory size in bytes
    static final int ENDOFF = 16;	// offset of first CEN header
    static final int ENDCOM = 20;	// zip file comment length
}
  1. 在包中新建ZipEntry类,代码如下:

ZipEntry.java:

package cn.edu.xdian.crytoll;
import java.util.Date;

/**
 * This class is used to represent a ZIP file entry.
 *
 * @version	1.42, 01/02/08
 * @author	David Connelly
 */
public
class ZipEntry implements ZipConstants, Cloneable {
    String name;	// entry name
    long time = -1;	// modification time (in DOS time)
    long crc = -1;	// crc-32 of entry data
    long size = -1;	// uncompressed size of entry data
    long csize = -1;   	// compressed size of entry data
    int method = -1;	// compression method
    byte[] extra;       // optional extra field data for entry
    String comment;     // optional comment string for entry

    /**
     * Compression method for uncompressed entries.
     */
    public static final int STORED = 0;

    /**
     * Compression method for compressed (deflated) entries.
     */
    public static final int DEFLATED = 8;

    static {
	/* Zip library is loaded from System.initializeSystemClass */
	//initIDs();
    }

    private static native void initIDs();

    /**
     * Creates a new zip entry with the specified name.
     *
     * @param name the entry name
     * @exception NullPointerException if the entry name is null
     * @exception IllegalArgumentException if the entry name is longer than
     *		  0xFFFF bytes
     */
    public ZipEntry(String name) {
	if (name == null) {
	    throw new NullPointerException();
	}
	if (name.length() > 0xFFFF) {
	    throw new IllegalArgumentException("entry name too long");
	}
	this.name = name;
    }

    /**
     * Creates a new zip entry with fields taken from the specified
     * zip entry.
     * @param e a zip Entry object
     */
    public ZipEntry(ZipEntry e) {
	name = e.name;
	time = e.time;
	crc = e.crc;
	size = e.size;
	csize = e.csize;
	method = e.method;
	extra = e.extra;
	comment = e.comment;
    }

    /*
     * Creates a new zip entry for the given name with fields initialized
     * from the specified jzentry data.
     */
    ZipEntry(String name, long jzentry) {
	this.name = name;
	initFields(jzentry);
    }

    private native void initFields(long jzentry);

    /*
     * Creates a new zip entry with fields initialized from the specified
     * jzentry data.
     */
    ZipEntry(long jzentry) {
	initFields(jzentry);
    }

    /**
     * Returns the name of the entry.
     * @return the name of the entry
     */
    public String getName() {
	return name;
    }

    /**
     * Sets the modification time of the entry.
     * @param time the entry modification time in number of milliseconds
     *		   since the epoch
     * @see #getTime()
     */
    public void setTime(long time) {
	    this.time = javaToDosTime(time);
    }

    /**
     * Returns the modification time of the entry, or -1 if not specified.
     * @return the modification time of the entry, or -1 if not specified
     * @see #setTime(long)
     */
    public long getTime() {
	return time != -1 ? dosToJavaTime(time) : -1;
    }

    /**
     * Sets the uncompressed size of the entry data.
     * @param size the uncompressed size in bytes
     * @exception IllegalArgumentException if the specified size is less
     *		  than 0 or greater than 0xFFFFFFFF bytes
     * @see #getSize()
     */
    public void setSize(long size) {
	if (size < 0 || size > 0xFFFFFFFFL) {
	    throw new IllegalArgumentException("invalid entry size");
	}
	this.size = size;
    }

    /**
     * Returns the uncompressed size of the entry data, or -1 if not known.
     * @return the uncompressed size of the entry data, or -1 if not known
     * @see #setSize(long)
     */
    public long getSize() {
	return size;
    }

    /**
     * Returns the size of the compressed entry data, or -1 if not known.
     * In the case of a stored entry, the compressed size will be the same
     * as the uncompressed size of the entry.
     * @return the size of the compressed entry data, or -1 if not known
     * @see #setCompressedSize(long)
     */
    public long getCompressedSize() {
	return csize;
    }

    /**
     * Sets the size of the compressed entry data.
     * @param csize the compressed size to set to
     * @see #getCompressedSize()
     */
    public void setCompressedSize(long csize) {
	this.csize = csize;
    }

    /**
     * Sets the CRC-32 checksum of the uncompressed entry data.
     * @param crc the CRC-32 value
     * @exception IllegalArgumentException if the specified CRC-32 value is
     *		  less than 0 or greater than 0xFFFFFFFF
     * @see #getCrc()
     */
    public void setCrc(long crc) {
	if (crc < 0 || crc > 0xFFFFFFFFL) {
	    throw new IllegalArgumentException("invalid entry crc-32");
	}
	this.crc = crc;
    }

    /**
     * Returns the CRC-32 checksum of the uncompressed entry data, or -1 if
     * not known.
     * @return the CRC-32 checksum of the uncompressed entry data, or -1 if
     * not known
     * @see #setCrc(long)
     */
    public long getCrc() {
	return crc;
    }

    /**
     * Sets the compression method for the entry.
     * @param method the compression method, either STORED or DEFLATED
     * @exception IllegalArgumentException if the specified compression
     *		  method is invalid
     * @see #getMethod()
     */
    public void setMethod(int method) {
	if (method != STORED && method != DEFLATED) {
	    throw new IllegalArgumentException("invalid compression method");
	}
	this.method = method;
    }

    /**
     * Returns the compression method of the entry, or -1 if not specified.
     * @return the compression method of the entry, or -1 if not specified
     * @see #setMethod(int)
     */
    public int getMethod() {
	return method;
    }

    /**
     * Sets the optional extra field data for the entry.
     * @param extra the extra field data bytes
     * @exception IllegalArgumentException if the length of the specified
     *		  extra field data is greater than 0xFFFF bytes
     * @see #getExtra()
     */
    public void setExtra(byte[] extra) {
	if (extra != null && extra.length > 0xFFFF) {
	    throw new IllegalArgumentException("invalid extra field length");
	}
	this.extra = extra;
    }

    /**
     * Returns the extra field data for the entry, or null if none.
     * @return the extra field data for the entry, or null if none
     * @see #setExtra(byte[])
     */
    public byte[] getExtra() {
	return extra;
    }

    /**
     * Sets the optional comment string for the entry.
     * @param comment the comment string
     * @exception IllegalArgumentException if the length of the specified
     *		  comment string is greater than 0xFFFF bytes
     * @see #getComment()
     */
    public void setComment(String comment) {
	if (comment != null && comment.length() > 0xffff/3
                    && ZipOutputStream.getUTF8Length(comment) > 0xffff) {
	    throw new IllegalArgumentException("invalid entry comment length");
	}
	this.comment = comment;
    }

    /**
     * Returns the comment string for the entry, or null if none.
     * @return the comment string for the entry, or null if none
     * @see #setComment(String)
     */
    public String getComment() {
	return comment;
    }

    /**
     * Returns true if this is a directory entry. A directory entry is
     * defined to be one whose name ends with a \'/\'.
     * @return true if this is a directory entry
     */
    public boolean isDirectory() {
	return name.endsWith("/");
    }

    /**
     * Returns a string representation of the ZIP entry.
     */
    public String toString() {
	return getName();
    }

    /*
     * Converts DOS time to Java time (number of milliseconds since epoch).
     */
    private static long dosToJavaTime(long dtime) {
	Date d = new Date((int)(((dtime >> 25) & 0x7f) + 80),
			  (int)(((dtime >> 21) & 0x0f) - 1),
			  (int)((dtime >> 16) & 0x1f),
			  (int)((dtime >> 11) & 0x1f),
			  (int)((dtime >> 5) & 0x3f),
			  (int)((dtime << 1) & 0x3e));
	return d.getTime();
    }

    /*
     * Converts Java time to DOS time.
     */
    private static long javaToDosTime(long time) {
	Date d = new Date(time);
	int year = d.getYear() + 1900;
	if (year < 1980) {
	    return (1 << 21) | (1 << 16);
	}
	return (year - 1980) << 25 | (d.getMonth() + 1) << 21 |
               d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 |
               d.getSeconds() >> 1;
    }

    /**
     * Returns the hash code value for this entry.
     */
    public int hashCode() {
	return name.hashCode();
    }

    /**
     * Returns a copy of this entry.
     */
    public Object clone() {
	try {
	    ZipEntry e = (ZipEntry)super.clone();
	    e.extra = (extra == null ? null : (byte[])extra.clone());
	    return e;
	} catch (CloneNotSupportedException e) {
	    // This should never happen, since we are Cloneable
	    throw new InternalError();
	}
    }
}

6. 在包中新建ZipInputStream类,代码如下:

ZipInputStream.java:

package cn.edu.xdian.crytoll;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.io.UnsupportedEncodingException;
import java.util.zip.CRC32;
import java.util.zip.Inflater;
import java.util.zip.ZipException;

/**
 * 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
 * @version	1.44, 06/15/07
 */
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;

    /**
     * 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.
     * @param in the actual input stream
     */
    public ZipInputStream(InputStream in) {
	super(new PushbackInputStream(in, 512), new Inflater(true), 512);
        usesDefaultInflater = true;
        if(in == null) {
            throw new NullPointerException("in is null");
        }
    }

    /**
     * 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
     * @exception ZipException if a ZIP file error has occurred
     * @exception 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.
     * @exception ZipException if a ZIP file error has occurred
     * @exception 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.
     * <p>
     * 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.
     * @exception  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</code> is not zero, the method
     * blocks until some input is available; otherwise, no
     * bytes are read and <code>0</code> is returned.
     * @param b the buffer into which the data is read
     * @param off the start offset in the destination array <code>b</code>
     * @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
     * @exception  NullPointerException If <code>b</code> is <code>null</code>.
     * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
     * <code>len</code> is negative, or <code>len</code> is greater than
     * <code>b.length - off</code>
     * @exception ZipException if a ZIP file error has occurred
     * @exception 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
     * @exception ZipException if a ZIP file error has occurred
     * @exception IOException if an I/O error has occurred
     * @exception IllegalArgumentException if 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.
     * @exception 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 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);
	ZipEntry e = createZipEntry(getUTF8String(b, 0, len));
	// now get the remaining fields for the entry
	flag = get16(tmpbuf, LOCFLG);
	if ((flag & 1) == 1) {
	    throw new ZipException("encrypted ZIP entry not supported");
	}
	e.method = get16(tmpbuf, LOCHOW);
	e.time = 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[] bb = new byte[len];
	    readFully(bb, 0, len);
	    e.setExtra(bb);
	}
	return e;
    }

    /*
     * Fetches a UTF8-encoded String from the specified byte array.
     */
    private static String getUTF8String(byte[] b, int off, int len) 
    {
        
        try
        {
            String s = new String(b, off, len, "GBK");
            return s;
        }
        catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
        }
        
        //以上为新添加的解决GBK乱码的
        
        // First, count the number of characters in the sequence
        int count = 0;
        int max = off + len;
        int i = off;
        while (i < max)
        {
            int c = b[i++] & 0xff;
            switch (c >> 4)
            {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                    // 0xxxxxxx
                    count++;
                    break;
                case 12:
                case 13:
                    // 110xxxxx 10xxxxxx
                    if ((int) (b[i++] & 0xc0) != 0x80)
                    {
                        throw new IllegalArgumentException();
                    }
                    count++;
                    break;
                case 14:
                    // 1110xxxx 10xxxxxx 10xxxxxx
                    if (((int) (b[i++] & 0xc0) != 0x80)
                            || ((int) (b[i++] & 0xc0) != 0x80))
                    {
                        throw new IllegalArgumentException();
                    }
                    count++;
                    break;
                default:
                    // 10xxxxxx, 1111xxxx
                    throw new IllegalArgumentException();
            }
        }
        if (i != max)
        {
            throw new IllegalArgumentException();
        }
        // Now decode the characters...
        char[] cs = new char[count];
        i = 0;
        while (off < max)
        {
            int c = b[off++] & 0xff;
            switch (c >> 4)
            {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                    // 0xxxxxxx
                    cs[i++] = (char) c;
                    break;
                case 12:
                case 13:
                    // 110xxxxx 10xxxxxx
                    cs[i++] = (char) (((c & 0x1f) << 6) | (b[off++] & 0x3f));
                    break;
                case 14:
                    // 1110xxxx 10xxxxxx 10xxxxxx
                    int t = (b[off++] & 0x3f) << 6;
                    cs[i++] = (char) (((c & 0x0f) << 12) | t | (b[off++] & 0x3f));
                    break;
                default:
                    // 10xxxxxx, 1111xxxx
                    throw new IllegalArgumentException();
            }
        }
        return new String(cs, 0, count);
    }

    /**
     * Creates a new <code>ZipEntry</code> 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.
     */
    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 */
	    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 - 1, 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;
	}
    }

    /*
     * Fetches unsigned 16-bit value from byte array at specified offset.
     * The bytes are assumed to be in Intel (little-endian) byte order.
     */
    private static final int get16(byte b[], int off) {
	return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8);
    }

    /*
     * Fetches unsigned 32-bit value from byte array at specified offset.
     * The bytes are assumed to be in Intel (little-endian) byte order.
     */
    private static final long get32(byte b[], int off) {
	return get16(b, off) | ((long)get16(b, off+2) << 16);
    }
}

7. 在包中新建ZipOutputStream类,代码如下:

ZipOutputStream.java:

package cn.edu.xdian.crytoll;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Vector;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.ZipException;

/**
 * This class implements an output stream filter for writing files in the
 * ZIP file format. Includes support for both compressed and uncompressed
 * entries.
 *
 * @author	David Connelly
 * @version	1.35, 07/31/06
 */
public
class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {

    private static class XEntry {
	public final ZipEntry entry;
	public final long offset;
	public final int flag;
	public XEntry(ZipEntry entry, long offset) {
	    this.entry = entry;
	    this.offset = offset;
	    this.flag = (entry.method == DEFLATED &&
			 (entry.size  == -1 ||
			  entry.csize == -1 ||
			  entry.crc   == -1))
		// store size, compressed size, and crc-32 in data descriptor
		// immediately following the compressed entry data
		? 8
		// store size, compressed size, and crc-32 in LOC header
		: 0;
	}
    }

    private XEntry current;
    private Vector<XEntry> xentries = new Vector<XEntry>();
    private HashSet<String> names = new HashSet<String>();
    private CRC32 crc = new CRC32();
    private long written = 0;
    private long locoff = 0;
    private String comment;
    private int method = DEFLATED;
    private boolean finished;

    private boolean closed = false;

    private static int version(ZipEntry e) throws ZipException {
	switch (e.method) {
	case DEFLATED: return 20;
	case STORED:   return 10;
	default: throw new ZipException("unsupported compression method");
	}
    }

    /**
     * Checks to make sure that this stream has not been closed.
     */
    private void ensureOpen() throws IOException {
	if (closed) {
	    throw new IOException("Stream closed");
        }
    }
    /**
     * Compression method for uncompressed (STORED) entries.
     */
    public static final int STORED = ZipEntry.STORED;

    /**
     * Compression method for compressed (DEFLATED) entries.
     */
    public static final int DEFLATED = ZipEntry.DEFLATED;

    /**
     * Creates a new ZIP output stream.
     * @param out the actual output stream
     */
    public ZipOutputStream(OutputStream out) {
	super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
        usesDefaultDeflater = true;
    }

    /**
     * Sets the ZIP file comment.
     * @param comment the comment string
     * @exception IllegalArgumentException if the length of the specified
     *		  ZIP file comment is greater than 0xFFFF bytes
     */
    public void setComment(String comment) {
        if (comment != null && comment.length() > 0xffff/3
                                           && getUTF8Length(comment) > 0xffff) {
	    throw new IllegalArgumentException("ZIP file comment too long.");
	}
	this.comment = comment;
    }

    /**
     * Sets the default compression method for subsequent entries. This
     * default will be used whenever the compression method is not specified
     * for an individual ZIP file entry, and is initially set to DEFLATED.
     * @param method the default compression method
     * @exception IllegalArgumentException if the specified compression method
     *		  is invalid
     */
    public void setMethod(int method) {
	if (method != DEFLATED && method != STORED) {
	    throw new IllegalArgumentException("invalid compression method");
	}
	this.method = method;
    }

    /**
     * Sets the compression level for subsequent entries which are DEFLATED.
     * The default setting is DEFAULT_COMPRESSION.
     * @param level the compression level (0-9)
     * @exception IllegalArgumentException if the compression level is invalid
     */
    public void setLevel(int level) {
	def.setLevel(level);
    }

    /**
     * Begins writing a new ZIP file entry and positions the stream to the
     * start of the entry data. Closes the current entry if still active.
     * The default compression method will be used if no compression method
     * was specified for the entry, and the current time will be used if
     * the entry has no set modification time.
     * @param e the ZIP entry to be written
     * @exception ZipException if a ZIP format error has occurred
     * @exception IOException if an I/O error has occurred
     */
    public void putNextEntry(ZipEntry e) throws IOException {
	ensureOpen();
	if (current != null) {
	    closeEntry();	// close previous entry
	}
	if (e.time == -1) {
	    e.setTime(System.currentTimeMillis());
	}
	if (e.method == -1) {
	    e.method = method;	// use default method
	}
	switch (e.method) {
	case DEFLATED:
	    break;
	case STORED:
	    // compressed size, uncompressed size, and crc-32 must all be
	    // set for entries using STORED compression method
	    if (e.size == -1) {
		e.size = e.csize;
	    } else if (e.csize == -1) {
		e.csize = e.size;
	    } else if (e.size != e.csize) {
		throw new ZipException(
		    "STORED entry where compressed != uncompressed size");
	    }
	    if (e.size == -1 || e.crc == -1) {
		throw new ZipException(
		    "STORED entry missing size, compressed size, or crc-32");
	    }
	    break;
	default:
	    throw new ZipException("unsupported compression method");
	}
	if (! names.add(e.name)) {
	    throw new ZipException("duplicate entry: " + e.name);
	}
	current = new XEntry(e, written);
	xentries.add(current);
        writeLOC(current);
    }

    /**
     * Closes the current ZIP entry and positions the stream for writing
     * the next entry.
     * @exception ZipException if a ZIP format error has occurred
     * @exception IOException if an I/O error has occurred
     */
    public void closeEntry() throws IOException {
	ensureOpen();
	if (current != null) {
	    ZipEntry e = current.entry;
	    switch (e.method) {
	    case DEFLATED:
		def.finish();
		while (!def.finished()) {
		    deflate();
		}
		if ((current.flag & 8) == 0) {
		    // verify size, compressed size, and crc-32 settings
		    if (e.size != def.getBytesRead()) {
			throw new ZipException(
			    "invalid entry size (expected " + e.size +
			    " but got " + def.getBytesRead() + " bytes)");
		    }
		    if (e.csize != def.getBytesWritten()) {
			throw new ZipException(
			    "invalid entry compressed size (expected " +
			    e.csize + " but got " + def.getBytesWritten() + " bytes)");
		    }
		    if (e.crc != crc.getValue()) {
			throw new ZipException(
			    "invalid entry CRC-32 (expected 0x" +
			    Long.toHexString(e.crc) + " but got 0x" +
			    Long.toHexString(crc.getValue()) + ")");
		    }
		} else {
		    e.size  = def.getBytesRead();
		    e.csize = def.getBytesWritten();
		    e.crc = crc.getValue();
		    writeEXT(e);
		}
		def.reset();
		written += e.csize;
		break;
	    case STORED:
		// we already know that both e.size and e.csize are the same
		if (e.size != written - locoff) {
		    throw new ZipException(
			"invalid entry size (expected " + e.size +
			" but got " + (written - locoff) + " bytes)");
		}
		if (e.crc != crc.getValue()) {
		    throw new ZipException(
			 "invalid entry crc-32 (expected 0x" +
			 Long.toHexString(e.crc) + " but got 0x" +
			 Long.toHexString(crc.getValue()) + ")");
		}
		break;
	    default:
		throw new ZipException("invalid compression method");
	    }
	    crc.reset();
	    current = null;
	}
    }

    /**
     * Writes an array of bytes to the current ZIP entry data. This method
     * will block until all the bytes are written.
     * @param b the data to be written
     * @param off the start offset in the data
     * @param len the number of bytes that are written
     * @exception ZipException if a ZIP file error has occurred
     * @exception IOException if an I/O error has occurred
     */
    public synchronized void write(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;
	}

	if (current == null) {
	    throw new ZipException("no current ZIP entry");
	}
	ZipEntry entry = current.entry;
	switch (entry.method) {
	case DEFLATED:
	    super.write(b, off, len);
	    break;
	case STORED:
	    written += len;
	    if (written - locoff > entry.size) {
		throw new ZipException(
		    "attempt to write past end of STORED entry");
	    }
	    out.write(b, off, len);
	    break;
	default:
	    throw new ZipException("invalid compression method");
	}
	crc.update(b, off, len);
    }

    /**
     * Finishes writing the contents of the ZIP output stream without closing
     * the underlying stream. Use this method when applying multiple filters
     * in succession to the same output stream.
     * @exception ZipException if a ZIP file error has occurred
     * @exception IOException if an I/O exception has occurred
     */
    public void finish() throws IOException {
	ensureOpen();
	if (finished) {
	    return;
	}
	if (current != null) {
	    closeEntry();
	}
	if (xentries.size() < 1) {
	    throw new ZipException("ZIP file must have at least one entry");
	}
	// write central directory
	long off = written;
	for (XEntry xentry : xentries)
	    writeCEN(xentry);
	writeEND(off, written - off);
	finished = true;
    }

    /**
     * Closes the ZIP output stream as well as the stream being filtered.
     * @exception ZipException if a ZIP file error has occurred
     * @exception IOException if an I/O error has occurred
     */
    public void close() throws IOException {
        if (!closed) {
            super.close();
            closed = true;
        }
    }

    /*
     * Writes local file (LOC) header for specified entry.
     */
    private void writeLOC(XEntry xentry) throws IOException {
	ZipEntry e = xentry.entry;
	int flag = xentry.flag;
	writeInt(LOCSIG);	    // LOC header signature
	writeShort(version(e));     // version needed to extract
	writeShort(flag);           // general purpose bit flag
	writeShort(e.method);       // compression method
	writeInt(e.time);           // last modification time
	if ((flag & 8) == 8) {
	    // store size, uncompressed size, and crc-32 in data descriptor
	    // immediately following compressed entry data
	    writeInt(0);
	    writeInt(0);
	    writeInt(0);
	} else {
	    writeInt(e.crc);        // crc-32
	    writeInt(e.csize);      // compressed size
	    writeInt(e.size);       // uncompressed size
	}
	byte[] nameBytes = getUTF8Bytes(e.name);
	writeShort(nameBytes.length);
	writeShort(e.extra != null ? e.extra.length : 0);
	writeBytes(nameBytes, 0, nameBytes.length);
	if (e.extra != null) {
	    writeBytes(e.extra, 0, e.extra.length);
	}
	locoff = written;
    }

    /*
     * Writes extra data descriptor (EXT) for specified entry.
     */
    private void writeEXT(ZipEntry e) throws IOException {
	writeInt(EXTSIG);	    // EXT header signature
	writeInt(e.crc);	    // crc-32
	writeInt(e.csize);	    // compressed size
	writeInt(e.size);	    // uncompressed size
    }

    /*
     * Write central directory (CEN) header for specified entry.
     * REMIND: add support for file attributes
     */
    private void writeCEN(XEntry xentry) throws IOException {
	ZipEntry e  = xentry.entry;
	int flag = xentry.flag;
	int version = version(e);
	writeInt(CENSIG);	    // CEN header signature
	writeShort(version);	    // version made by
	writeShort(version);	    // version needed to extract
	writeShort(flag);	    // general purpose bit flag
	writeShort(e.method);	    // compression method
	writeInt(e.time);	    // last modification time
	writeInt(e.crc);	    // crc-32
	writeInt(e.csize);	    // compressed size
	writeInt(e.size);	    // uncompressed size
	byte[] nameBytes = getUTF8Bytes(e.name);
	writeShort(nameBytes.length);
	writeShort(e.extra != null ? e.extra.length : 0);
	byte[] commentBytes;
	if (e.comment != null) {
	    commentBytes = getUTF8Bytes(e.comment);
	    writeShort(commentBytes.length);
	} else {
	    commentBytes = null;
	    writeShort(0);
	}
	writeShort(0);		    // starting disk number
	writeShort(0);		    // internal file attributes (unused)
	writeInt(0);		    // external file attributes (unused)
	writeInt(xentry.offset);    // relative offset of local header
	writeBytes(nameBytes, 0, nameBytes.length);
	if (e.extra != null) {
	    writeBytes(e.extra, 0, e.extra.length);
	}
	if (commentBytes != null) {
	    writeBytes(commentBytes, 0, commentBytes.length);
	}
    }

    /*
     * Writes end of central directory (END) header.
     */
    private void writeEND(long off, long len) throws IOException {
	int count = xentries.size();
	writeInt(ENDSIG);	    // END record signature
	writeShort(0);		    // number of this disk
	writeShort(0);		    // central directory start disk
	writeShort(count);	    // number of directory entries on disk
	writeShort(count);	    // total number of directory entries
	writeInt(len);		    // length of central directory
	writeInt(off);		    // offset of central directory
	if (comment != null) {	    // zip file comment
	    byte[] b = getUTF8Bytes(comment);
	    writeShort(b.length);
	    writeBytes(b, 0, b.length);
	} else {
	    writeShort(0);
	}
    }

    /*
     * Writes a 16-bit short to the output stream in little-endian byte order.
     */
    private void writeShort(int v) throws IOException {
	OutputStream out = this.out;
	out.write((v >>> 0) & 0xff);
	out.write((v >>> 8) & 0xff);
	written += 2;
    }

    /*
     * Writes a 32-bit int to the output stream in little-endian byte order.
     */
    private void writeInt(long v) throws IOException {
	OutputStream out = this.out;
	out.write((int)((v >>>  0) & 0xff));
	out.write((int)((v >>>  8) & 0xff));
	out.write((int)((v >>> 16) & 0xff));
	out.write((int)((v >>> 24) & 0xff));
	written += 4;
    }

    /*
     * Writes an array of bytes to the output stream.
     */
    private void writeBytes(byte[] b, int off, int len) throws IOException {
	super.out.write(b, off, len);
	written += len;
    }

    /*
     * Returns the length of String\'s UTF8 encoding.
     */
    static int getUTF8Length(String s) {
        int count = 0;
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (ch <= 0x7f) {
                count++;
            } else if (ch <= 0x7ff) {
                count += 2;
            } else {
                count += 3;
            }
        }
        return count;
    }

    /*
     * Returns an array of bytes representing the UTF8 encoding
     * of the specified String.
     */
    private static byte[] getUTF8Bytes(String s) {
	char[] c = s.toCharArray();
	int len = c.length;
	// Count the number of encoded bytes...
	int count = 0;
	for (int i = 0; i < len; i++) {
	    int ch = c[i];
	    if (ch <= 0x7f) {
		count++;
	    } else if (ch <= 0x7ff) {
		count += 2;
	    } else {
		count += 3;
	    }
	}
	// Now return the encoded bytes...
	byte[] b = new byte[count];
	int off = 0;
	for (int i = 0; i < len; i++) {
	    int ch = c[i];
	    if (ch <= 0x7f) {
		b[off++] = (byte)ch;
	    } else if (ch <= 0x7ff) {
		b[off++] = (byte)((ch >> 6) | 0xc0);
		b[off++] = (byte)((ch & 0x3f) | 0x80);
	    } else {
		b[off++] = (byte)((ch >> 12) | 0xe0);
		b[off++] = (byte)(((ch >> 6) & 0x3f) | 0x80);
		b[off++] = (byte)((ch & 0x3f) | 0x80);
	    }
	}
	return b;
    }
}

8. 新建一个Application Window,代码如下:

 

package cn.edu.xdian.crytoll;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;

import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
  
import cn.edu.xdian.crytoll.ZipEntry;  
import cn.edu.xdian.crytoll.ZipInputStream;  
import cn.edu.xdian.crytoll.ZipOutputStream; 


/**
 * 获取文件列表的过滤器
 * 
 * @author 李钟尉
 */
public class UnZipTextFileFrame extends JFrame {
    private JPanel contentPane;
    private JTextField forderField;
    private JTextField templetField;
    private File file;
    private File dir;
    private JTable table;
    private JTextField extNameField;
    private JSpinner startSpinner;
    private JTextField textField;
    private JTextField textField_1;
    private DefaultTableModel model;
    private String filesrc;
    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                	UnZipTextFileFrame frame = new UnZipTextFileFrame();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
    
    /**
     * Create the frame.
     */
    public UnZipTextFileFrame() {
        setResizable(false);
        setTitle("压缩包解压到指定文件夹");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 500, 300);
        getContentPane().setLayout(null);
        
        textField = new JTextField();
        textField.setBounds(10, 10, 158, 21);
        getContentPane().add(textField);
        textField.setColumns(10);
        
        JButton btnZip = new JButton("Zip\u6587\u4EF6");
        btnZip.addActionListener(new ActionListener() {
        	public void actionPerformed(ActionEvent e) {
        		do_btnZip_actionPerformed(e);
        	}
        });
        btnZip.setBounds(178, 9, 93, 23);
        getContentPane().add(btnZip);
        
        textField_1 = new JTextField();
        textField_1.setBounds(281, 10, 100, 21);
        getContentPane().add(textField_1);
        textField_1.setColumns(10);
        
        JButton btnNewButton = new JButton("解压到");
        btnNewButton.addActionListener(new ActionListener() {
        	public void actionPerformed(ActionEvent e) {
        		do_btnNewButton_actionPerformed(e);
        	}
        });
        btnNewButton.setBounds(391, 9, 93, 23);
        getContentPane().add(btnNewButton);
        
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setBounds(10, 41, 474, 170);
        getContentPane().add(scrollPane);
        
        table = new JTable();
        scrollPane.setViewportView(table);
        model= (DefaultTableModel) table.getModel();
        model.setColumnIdentifiers(new Object[] { "序号", "文件名"});
        JButton button = new JButton("\u5F00\u59CB\u89E3\u538B\u7F29");
        button.addActionListener(new ActionListener() {
        	public void actionPerformed(ActionEvent e) {
        		do_button_actionPerformed(e);
        	}
        });
        button.setBounds(178, 221, 100, 23);
        getContentPane().add(button);
        
    }
    protected void do_btnZip_actionPerformed(ActionEvent e){
    	JFileChooser chooser = new JFileChooser();// 创建文件选择器
        int option = chooser.showOpenDialog(this);// 显示文件打开对话框
        if (option == JFileChooser.APPROVE_OPTION) {
            file = chooser.getSelectedFile();// 获取选择的文件数组
            filesrc=file.getAbsolutePath();
            textField.setText(filesrc);// 清空文本框
        } else {
        	textField.setText("");// 清空文本框
        }
    }
    protected void do_btnNewButton_actionPerformed(ActionEvent e){
    	JFileChooser chooser = new JFileChooser();// 创建文件选择器
        // 设置选择器只针对文件夹生效
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        int option = chooser.showOpenDialog(this);// 显示文件打开对话框
        if (option == JFileChooser.APPROVE_OPTION) {
            dir = chooser.getSelectedFile();// 获取选择的文件夹
            textField_1.setText(dir.toString());// 显示文件夹到文本框
        } else {
            dir = null;
            textField_1.setText("");
        }
    }
    protected void do_button_actionPerformed(ActionEvent e){
    	ZipInputStream zin;    	
    	try{
    		//filesrc=new String(filesrc.getBytes("ISO-8859-1"),"UTF-8");
    		FileInputStream in = new FileInputStream(filesrc);
    		zin=new ZipInputStream(in);
    		ZipEntry entry;
    		int i=1;
    		while(((entry=zin.getNextEntry())!=null)&&!entry.isDirectory()){
    			File file=new File(dir.toString()+"/"+entry.getName());
    			if(!file.exists()){
    				file.createNewFile();
    			}
    			zin.closeEntry();
    			Object[] property = new Object[2];
    			property[0] = i;
    			property[1] = entry.getName();
    			model.addRow(property);
    			i++;
    		}
    	}catch(Exception ex){
    		ex.printStackTrace();
    	}    	
    }
}

 

  效果如图: