首页 > 其他 > 详细

Java压缩包解压到指定文件

时间:2014-03-02 17:44:40      阅读:814      评论:0      收藏:0      [点我收藏+]

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

bubuko.com,布布扣

    

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

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

DeflaterOutputStream.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
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,代码如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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();
        }      
    }
}

 

  效果如图:

bubuko.com,布布扣

Java压缩包解压到指定文件,布布扣,bubuko.com

Java压缩包解压到指定文件

原文:http://www.cnblogs.com/cysolo/p/3575466.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!