1 7 8 package java.util.zip; 9 10 import java.io.OutputStream ; 11 import java.io.IOException ; 12 import java.util.Vector ; 13 import java.util.Hashtable ; 14 import java.util.Enumeration ; 15 16 24 public 25 class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { 26 private ZipEntry entry; 27 private Vector entries = new Vector (); 28 private Hashtable names = new Hashtable (); 29 private CRC32 crc = new CRC32 (); 30 private long written = 0; 31 private long locoff = 0; 32 private String comment; 33 private int method = DEFLATED; 34 private boolean finished; 35 36 private boolean closed = false; 37 38 41 private void ensureOpen() throws IOException { 42 if (closed) { 43 throw new IOException ("Stream closed"); 44 } 45 } 46 49 public static final int STORED = ZipEntry.STORED; 50 51 54 public static final int DEFLATED = ZipEntry.DEFLATED; 55 56 60 public ZipOutputStream(OutputStream out) { 61 super(out, new Deflater (Deflater.DEFAULT_COMPRESSION, true)); 62 usesDefaultDeflater = true; 63 } 64 65 71 public void setComment(String comment) { 72 if (comment != null && comment.length() > 0xffff/3 73 && getUTF8Length(comment) > 0xffff) { 74 throw new IllegalArgumentException ("ZIP file comment too long."); 75 } 76 this.comment = comment; 77 } 78 79 87 public void setMethod(int method) { 88 if (method != DEFLATED && method != STORED) { 89 throw new IllegalArgumentException ("invalid compression method"); 90 } 91 this.method = method; 92 } 93 94 100 public void setLevel(int level) { 101 def.setLevel(level); 102 } 103 104 114 public void putNextEntry(ZipEntry e) throws IOException { 115 ensureOpen(); 116 if (entry != null) { 117 closeEntry(); } 119 if (e.time == -1) { 120 e.setTime(System.currentTimeMillis()); 121 } 122 if (e.method == -1) { 123 e.method = method; } 125 switch (e.method) { 126 case DEFLATED: 127 if (e.size == -1 || e.csize == -1 || e.crc == -1) { 128 e.flag = 8; 131 } else if (e.size != -1 && e.csize != -1 && e.crc != -1) { 132 e.flag = 0; 134 } else { 135 throw new ZipException ( 136 "DEFLATED entry missing size, compressed size, or crc-32"); 137 } 138 e.version = 20; 139 break; 140 case STORED: 141 if (e.size == -1) { 144 e.size = e.csize; 145 } else if (e.csize == -1) { 146 e.csize = e.size; 147 } else if (e.size != e.csize) { 148 throw new ZipException ( 149 "STORED entry where compressed != uncompressed size"); 150 } 151 if (e.size == -1 || e.crc == -1) { 152 throw new ZipException ( 153 "STORED entry missing size, compressed size, or crc-32"); 154 } 155 e.version = 10; 156 e.flag = 0; 157 break; 158 default: 159 throw new ZipException ("unsupported compression method"); 160 } 161 e.offset = written; 162 if (names.put(e.name, e) != null) { 163 throw new ZipException ("duplicate entry: " + e.name); 164 } 165 writeLOC(e); 166 entries.addElement(e); 167 entry = e; 168 } 169 170 176 public void closeEntry() throws IOException { 177 ensureOpen(); 178 ZipEntry e = entry; 179 if (e != null) { 180 switch (e.method) { 181 case DEFLATED: 182 def.finish(); 183 while (!def.finished()) { 184 deflate(); 185 } 186 if ((e.flag & 8) == 0) { 187 if (e.size != def.getBytesRead()) { 189 throw new ZipException ( 190 "invalid entry size (expected " + e.size + 191 " but got " + def.getBytesRead() + " bytes)"); 192 } 193 if (e.csize != def.getBytesWritten()) { 194 throw new ZipException ( 195 "invalid entry compressed size (expected " + 196 e.csize + " but got " + def.getBytesWritten() + " bytes)"); 197 } 198 if (e.crc != crc.getValue()) { 199 throw new ZipException ( 200 "invalid entry CRC-32 (expected 0x" + 201 Long.toHexString(e.crc) + " but got 0x" + 202 Long.toHexString(crc.getValue()) + ")"); 203 } 204 } else { 205 e.size = def.getBytesRead(); 206 e.csize = def.getBytesWritten(); 207 e.crc = crc.getValue(); 208 writeEXT(e); 209 } 210 def.reset(); 211 written += e.csize; 212 break; 213 case STORED: 214 if (e.size != written - locoff) { 216 throw new ZipException ( 217 "invalid entry size (expected " + e.size + 218 " but got " + (written - locoff) + " bytes)"); 219 } 220 if (e.crc != crc.getValue()) { 221 throw new ZipException ( 222 "invalid entry crc-32 (expected 0x" + 223 Long.toHexString(e.crc) + " but got 0x" + 224 Long.toHexString(crc.getValue()) + ")"); 225 } 226 break; 227 default: 228 throw new InternalError ("invalid compression method"); 229 } 230 crc.reset(); 231 entry = null; 232 } 233 } 234 235 244 public synchronized void write(byte[] b, int off, int len) 245 throws IOException 246 { 247 ensureOpen(); 248 if (off < 0 || len < 0 || off > b.length - len) { 249 throw new IndexOutOfBoundsException (); 250 } else if (len == 0) { 251 return; 252 } 253 254 if (entry == null) { 255 throw new ZipException ("no current ZIP entry"); 256 } 257 switch (entry.method) { 258 case DEFLATED: 259 super.write(b, off, len); 260 break; 261 case STORED: 262 written += len; 263 if (written - locoff > entry.size) { 264 throw new ZipException ( 265 "attempt to write past end of STORED entry"); 266 } 267 out.write(b, off, len); 268 break; 269 default: 270 throw new InternalError ("invalid compression method"); 271 } 272 crc.update(b, off, len); 273 } 274 275 282 public void finish() throws IOException { 283 ensureOpen(); 284 if (finished) { 285 return; 286 } 287 if (entry != null) { 288 closeEntry(); 289 } 290 if (entries.size() < 1) { 291 throw new ZipException ("ZIP file must have at least one entry"); 292 } 293 long off = written; 295 Enumeration e = entries.elements(); 296 while (e.hasMoreElements()) { 297 writeCEN((ZipEntry )e.nextElement()); 298 } 299 writeEND(off, written - off); 300 finished = true; 301 } 302 303 308 public void close() throws IOException { 309 if (!closed) { 310 super.close(); 311 closed = true; 312 } 313 } 314 315 318 private void writeLOC(ZipEntry e) throws IOException { 319 writeInt(LOCSIG); writeShort(e.version); writeShort(e.flag); writeShort(e.method); writeInt(e.time); if ((e.flag & 8) == 8) { 325 writeInt(0); 328 writeInt(0); 329 writeInt(0); 330 } else { 331 writeInt(e.crc); writeInt(e.csize); writeInt(e.size); } 335 byte[] nameBytes = getUTF8Bytes(e.name); 336 writeShort(nameBytes.length); 337 writeShort(e.extra != null ? e.extra.length : 0); 338 writeBytes(nameBytes, 0, nameBytes.length); 339 if (e.extra != null) { 340 writeBytes(e.extra, 0, e.extra.length); 341 } 342 locoff = written; 343 } 344 345 348 private void writeEXT(ZipEntry e) throws IOException { 349 writeInt(EXTSIG); writeInt(e.crc); writeInt(e.csize); writeInt(e.size); } 354 355 359 private void writeCEN(ZipEntry e) throws IOException { 360 writeInt(CENSIG); writeShort(e.version); writeShort(e.version); writeShort(e.flag); writeShort(e.method); writeInt(e.time); writeInt(e.crc); writeInt(e.csize); writeInt(e.size); byte[] nameBytes = getUTF8Bytes(e.name); 370 writeShort(nameBytes.length); 371 writeShort(e.extra != null ? e.extra.length : 0); 372 byte[] commentBytes; 373 if (e.comment != null) { 374 commentBytes = getUTF8Bytes(e.comment); 375 writeShort(commentBytes.length); 376 } else { 377 commentBytes = null; 378 writeShort(0); 379 } 380 writeShort(0); writeShort(0); writeInt(0); writeInt(e.offset); writeBytes(nameBytes, 0, nameBytes.length); 385 if (e.extra != null) { 386 writeBytes(e.extra, 0, e.extra.length); 387 } 388 if (commentBytes != null) { 389 writeBytes(commentBytes, 0, commentBytes.length); 390 } 391 } 392 393 396 private void writeEND(long off, long len) throws IOException { 397 writeInt(ENDSIG); writeShort(0); writeShort(0); writeShort(entries.size()); writeShort(entries.size()); writeInt(len); writeInt(off); if (comment != null) { byte[] b = getUTF8Bytes(comment); 406 writeShort(b.length); 407 writeBytes(b, 0, b.length); 408 } else { 409 writeShort(0); 410 } 411 } 412 413 416 private void writeShort(int v) throws IOException { 417 OutputStream out = this.out; 418 out.write((v >>> 0) & 0xff); 419 out.write((v >>> 8) & 0xff); 420 written += 2; 421 } 422 423 426 private void writeInt(long v) throws IOException { 427 OutputStream out = this.out; 428 out.write((int)((v >>> 0) & 0xff)); 429 out.write((int)((v >>> 8) & 0xff)); 430 out.write((int)((v >>> 16) & 0xff)); 431 out.write((int)((v >>> 24) & 0xff)); 432 written += 4; 433 } 434 435 438 private void writeBytes(byte[] b, int off, int len) throws IOException { 439 super.out.write(b, off, len); 440 written += len; 441 } 442 443 446 static int getUTF8Length(String s) { 447 int count = 0; 448 for (int i = 0; i < s.length(); i++) { 449 char ch = s.charAt(i); 450 if (ch <= 0x7f) { 451 count++; 452 } else if (ch <= 0x7ff) { 453 count += 2; 454 } else { 455 count += 3; 456 } 457 } 458 return count; 459 } 460 461 465 private static byte[] getUTF8Bytes(String s) { 466 char[] c = s.toCharArray(); 467 int len = c.length; 468 int count = 0; 470 for (int i = 0; i < len; i++) { 471 int ch = c[i]; 472 if (ch <= 0x7f) { 473 count++; 474 } else if (ch <= 0x7ff) { 475 count += 2; 476 } else { 477 count += 3; 478 } 479 } 480 byte[] b = new byte[count]; 482 int off = 0; 483 for (int i = 0; i < len; i++) { 484 int ch = c[i]; 485 if (ch <= 0x7f) { 486 b[off++] = (byte)ch; 487 } else if (ch <= 0x7ff) { 488 b[off++] = (byte)((ch >> 6) | 0xc0); 489 b[off++] = (byte)((ch & 0x3f) | 0x80); 490 } else { 491 b[off++] = (byte)((ch >> 12) | 0xe0); 492 b[off++] = (byte)(((ch >> 6) & 0x3f) | 0x80); 493 b[off++] = (byte)((ch & 0x3f) | 0x80); 494 } 495 } 496 return b; 497 } 498 } 499 | Popular Tags |