在获得一个以Zip格式压缩的文件之后,需要将其进行解压缩,还原成压缩前的文件。若是使用Java自带的压缩工具包来实现解压缩文件到指定文件夹的功能,因为jdk提供的zip只能按UTF-8格式处理,而Windows系统中文件名是以GBK方式编码的,所以如果是解压一个包含中文文件名的zip包,会报非法参数异常,如图所示:
所以要实现解压缩,就得对DeflaterOutputStream.java、InflaterInputStream.java、ZipConstants.java、ZipEntry.java、ZipInputStream.java以及ZipOutputStream.java这些相关的类进行修改,过程如下:
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 } |
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(); } } } |
效果如图:
原文:http://www.cnblogs.com/cysolo/p/3575466.html