`
hz_chenwenbiao
  • 浏览: 996169 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

java压缩文件,中文问题

阅读更多
   今天有同学问起来用java做压缩和解压缩的程序时,出现中文问题,我以前做过,不过已经很久了,那里又没有写日志,所以也忘记了自己所做的压缩小程序,今天又重新写一编,真是很浪费时间,平时要多做笔记,以后用到时就可以顺手拿来,不然跟白学一样,一切从头再来,切记切记。
    这里是用java.util.zip.ZipOutputStream来做压缩的话会出现将中文名字的文件一缩后,在压缩包里就会出现乱码的文件名,解决的办法可以修改java.util.zip.ZipOutputStream这个类,加入编码方式就可以,具体如下:
my.java.util.zip.ZipOutputStream

/*
 * 
 *
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package my.java.util.zip;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.ZipException;

public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
	private ZipEntry entry;
	private Vector entries = new Vector();
	private Hashtable names = new Hashtable();
	private CRC32 crc = new CRC32();
	private long written;
	private long locoff = 0;
	private String comment;
	private int method = DEFLATED;
	private boolean finished;
	private String encoding = "UTF-8"; // 为了支持中文,添加

	private boolean closed = false;

	/**
	 * Check 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;
	}

	/**
	 * Creates a new ZIP output stream.
	 * 
	 * @param out
	 *            the actual output stream
	 * @param encoding
	 *            set stream's code 为了支持中文添加
	 */
	public ZipOutputStream(OutputStream out, String encoding) {
		super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
		usesDefaultDeflater = true;
		this.encoding = encoding;
	}

	/**
	 * 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 (entry != 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:
			if (e.size == -1 || e.csize == -1 || e.crc == -1) {
				// store size, compressed size, and crc-32 in data descriptor
				// immediately following the compressed entry data
				e.flag = 8;
			} else if (e.size != -1 && e.csize != -1 && e.crc != -1) {
				// store size, compressed size, and crc-32 in LOC header
				e.flag = 0;
			} else {
				throw new ZipException(
						"DEFLATED entry missing size, compressed size, or crc-32");
			}
			e.version = 20;
			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");
			}
			e.version = 10;
			e.flag = 0;
			break;
		default:
			throw new ZipException("unsupported compression method");
		}
		e.offset = written;
		if (names.put(e.name, e) != null) {
			throw new ZipException("duplicate entry: " + e.name);
		}
		writeLOC(e);
		entries.addElement(e);
		entry = e;
	}

	/**
	 * 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();
		ZipEntry e = entry;
		if (e != null) {
			switch (e.method) {
			case DEFLATED:
				def.finish();
				while (!def.finished()) {
					deflate();// defate意思:漏气; (使)…瘪下去
				}
				if ((e.flag & 8) == 0) {
					// verify size, compressed size, and crc-32 settings
					if (e.size != def.getTotalIn()) {
						throw new ZipException("invalid entry size (expected "
								+ e.size + " but got " + def.getTotalIn()
								+ " bytes)");
					}
					if (e.csize != def.getTotalOut()) {
						throw new ZipException(
								"invalid entry compressed size (expected "
										+ e.csize + " but got "
										+ def.getTotalOut() + " 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.getTotalIn();
					e.csize = def.getTotalOut();
					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 InternalError("invalid compression method");
			}
			crc.reset();
			entry = 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 (entry == null) {
			throw new ZipException("no current ZIP 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 InternalError("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 (entry != null) {
			closeEntry();
		}
		if (entries.size() < 1) {
			throw new ZipException("ZIP file must have at least one entry");
		}
		// write central directory
		long off = written;
		Enumeration e = entries.elements();
		while (e.hasMoreElements()) {
			writeCEN((ZipEntry) e.nextElement());
		}
		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(ZipEntry e) throws IOException {
		writeInt(LOCSIG); // LOC header signature
		writeShort(e.version); // version needed to extract
		writeShort(e.flag); // general purpose bit flag
		writeShort(e.method); // compression method
		writeInt(e.time); // last modification time
		if ((e.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);
		// 为了支持中文,添加 begin
		byte[] nameBytes = null;
		try {
			if (this.encoding.toUpperCase().equals("UTF-8"))
				nameBytes = getUTF8Bytes(e.name);
			else
				nameBytes = e.name.getBytes(this.encoding);
		} catch (Exception byteE) {
			nameBytes = getUTF8Bytes(e.name);
		}
		// 为了支持中文,添加 end
		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(ZipEntry e) throws IOException {
		writeInt(CENSIG); // CEN header signature
		writeShort(e.version); // version made by
		writeShort(e.version); // version needed to extract
		writeShort(e.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);
		// 为了支持中文,添加 begin
		byte[] nameBytes = null;
		try {
			if (this.encoding.toUpperCase().equals("UTF-8"))
				nameBytes = getUTF8Bytes(e.name);
			else
				nameBytes = e.name.getBytes(this.encoding);
		} catch (Exception byteE) {
			nameBytes = getUTF8Bytes(e.name);
		}
		// 为了支持中文,添加 end
		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(e.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 {
		writeInt(ENDSIG); // END record signature
		writeShort(0); // number of this disk
		writeShort(0); // central directory start disk
		writeShort(entries.size()); // number of directory entries on disk
		writeShort(entries.size()); // 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;
	}
}

同时也要修改java.util.zip.ZipEntry这个类,主要是增加了三个属性:
int flag;
int version;
long offset;
在修改的ZipOutputStream类里会用到,具体ZipEntry代码如下:
my.java.util.zip.ZipEntry
/*
 * 
 *
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package my.java.util.zip;

import java.util.Date;

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
	// The following flags are used only by Zip{Input,Output}Stream
	int flag; // bit flags
	int version; // version needed to extract
	long offset; // offset of loc header

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

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

	// static {
	// /* load the zip library */
	// java.security.AccessController.doPrivileged(
	// new sun.security.action.LoadLibraryAction("zip"));
	// //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
	 */

	/**
	 * 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 #setCrc(long)
	 */
	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 #getCrc()
	 */
	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();
		}
	}
}


有了上面那两个类后,我们就不用java.util.zip包下的这两个类来做压缩,而是用来面的修改后的类来做,我写了一个简单的测试程序如下:
package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import my.java.util.zip.ZipEntry;
import my.java.util.zip.ZipOutputStream;

public class zip {
	private ZipOutputStream zipOutputStream;
	private FileOutputStream fos = null;
	
	public zip(File in , File out)
	{
		try {
			fos = new FileOutputStream(out);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		zipOutputStream = new ZipOutputStream(fos,"GBK");//只能是GBK,用UTF-8不行
		
		doZip(in, zipOutputStream, null);
		
		closeZipOutputStream();
	}
	
	/**
	 * 用递归调用的方式将一个文件夹下的所有文件压缩进压缩包里
	 * @param  input 输入要压缩的文件的路径
	 * @param zos 压缩输出流
	 * @param relativePath 文件或文件夹在压缩包里的相对路径,一开始应将相对路径设置为null
	 */
	public void doZip(File input , ZipOutputStream zos , String relativePath)
	{
		FileInputStream fis;
		byte[] b = new byte[1024];
		
		try {
			
			if(input.isDirectory())
			{
				relativePath = relativePath == null ? input.getName() : relativePath  + "/" + input.getName();
			
				zos.putNextEntry(new ZipEntry(relativePath + "/"));//ZipEntry应该是用来设置压缩文件所存放的相对路径的,当是文件夹时一定要后面加上"/",是文件则不用加
				
				File[] fileList = input.listFiles();
				for(int i = 0 ; i < fileList.length ; i++)
				{
					if(fileList[i].isDirectory())
					{
						doZip(fileList[i] , zos , relativePath);
					}
					else 
					{	
						zos.putNextEntry(new ZipEntry(relativePath + "/" +  fileList[i].getName()));//这个一定不要忘记了,不然文件是压缩不进入的哦
						fis = new FileInputStream(fileList[i]);
						while(fis.read(b) != -1)
						{
							zos.write(b);
						}
						fis.close();
					}
				}
			}
			else {
				
				relativePath = relativePath == null ? input.getName() : relativePath + "/" +  input.getName();
			
				zos.putNextEntry(new ZipEntry(relativePath));//文件不用加上"/"
				
				fis = new FileInputStream(input);
				while(fis.read(b) != -1)
				{
					zos.write(b);
				}
				fis.close();
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//关闭输出流
	public void closeZipOutputStream()
	{
		try {
			zipOutputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//简单测试
	public static void main(String[] args)
	{
		zip test = new zip(new File("F:\\MQ") , new File("F:/MQ.zip"));
		
		System.out.println("压缩完成");
	}

}


解压程序我也写了,如下:

package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import my.java.util.zip.ZipEntry;
import my.java.util.zip.ZipInputStream;

//解压简单例子
public class unZip {

	File in;
	File out;
	private ZipInputStream zis;
	
	public unZip(File in , File out)
	{
		this.in = in;
		this.out = out;
		initial();
	}
	
	public void initial()
	{
		//下面是用来检查输入的文件是否是zip文件类型用的,若输入的是.rar类型也会报异常
		try {
			ZipFile zipFile = new ZipFile(in);
			zipFile.close();
		} catch (ZipException e1) {
			e1.printStackTrace();
			return ;
		} catch (IOException e1) {
			e1.printStackTrace();
			return ;
		}
		
		if(out == null || !out.isDirectory())
		{
			System.out.println("请选择正确的解压存放的目录!");
			return ;
		}
		
		try {
			zis = new ZipInputStream(new FileInputStream(in),"GBK");//支持中文的地方
			doUnZip();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return ;
		}
	}
	
	public void doUnZip()
	{
		String pathName;
		ZipEntry zipEntry;
		File output;
		FileOutputStream fos;
		byte[] b;
		int len;
		String desPath;//存放的目标路径
		String tempPath;
		
		desPath = out.getAbsolutePath();
		
		try {
			zipEntry = zis.getNextEntry();//用它可以遍历在压缩包里的所有条目(包括文件和文件夹都会识别出来)
			while(zipEntry != null)
			{
				System.out.println(zipEntry.getName());
				
				tempPath = zipEntry.getName();
				
				//这里的文件路径用"\\"和"/"混合也是可以正确的创建目录或访问目录等,还是比较方便的
				if(desPath.endsWith("\\") || desPath.endsWith("/"))
					tempPath = desPath + tempPath;
				else 
					tempPath = desPath + File.separator + tempPath;
			
				output = new File(tempPath);
				
				if(zipEntry.isDirectory())//这里注意啦,不是output.isDirectory()来判断,是用ZipEntry来判断
				{
					/*
					 * File类的mkdir()和mkdirs()区别
					 * 简单来说,mkdir()就是创建一个目录,但前提是要创建的目录的父目录一定要存在。 例如:要创建D:\myeclipseprg7\CompilerTest\WebRoot\works 这个目录,那么D:\myeclipseprg7\CompilerTest\WebRoot\这个目录就一定要存在,否则用mkdir()无法成功创建目录。如果父目录不存在,我们可以用mkdirs(),这样不管父目录是否存在,都能创建成功。这样看来,似乎mkdir()这个函数没多大用处,今后建议大家只使用mkdirs()。
					 */
					output.mkdirs();
				}
				else//对于文件就直接输出 
				{
					fos = new FileOutputStream(output);
				    b = new byte[1024];
				    
				    while( (len = zis.read(b)) != -1)
				    {
				    	fos.write(b, 0, len);
				    }
					fos.close();
				}
				
				zipEntry = zis.getNextEntry();//下一条条目
			}
			
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}
		
		closeZipInputStream();
	}
	
	public void closeZipInputStream()
	{
		try {
			zis.close();
		} catch (IOException e) {
			e.printStackTrace();
			return ;
		}
	}
	
	public static void main(String[] args)
	{
		unZip test = new unZip(new File("F:\\chenwenbiao.zip"), new File("F:\\"));
	}
	
}


我以为用修改后的my.java.util.zip.ZipOutputStream或my.java.util.zip.ZipInputStream就可以解决问题,我将这两个类拷给同学,在他那里会出错,原来这两个类修改也包括了对它们引用到的类的修改,我现将它们打包发上来,导入就可以用了,中文名的压缩和解压缩的问题也可以解决了。
分享到:
评论
2 楼 suni0000 2013-11-18  
 
1 楼 containsoft 2010-10-18  
写的很详细,感激!

相关推荐

Global site tag (gtag.js) - Google Analytics