595 if (e.mtime == -1)
596 e.mtime = System.currentTimeMillis();
597 if (e.method == -1)
598 e.method = defaultMethod;
599 // store size, compressed size, and crc-32 in datadescriptor
600 e.flag = FLAG_DATADESCR;
601 if (zc.isUTF8())
602 e.flag |= FLAG_USE_UTF8;
603 }
604
605 @Override
606 public void close() throws IOException {
607 e.bytes = toByteArray();
608 e.size = e.bytes.length;
609 e.crc = -1;
610 super.close();
611 update(e);
612 }
613 }
614
615 // Returns a Writable/ReadByteChannel for now. Might consdier to use
616 // newFileChannel() instead, which dump the entry data into a regular
617 // file on the default file system and create a FileChannel on top of
618 // it.
619 SeekableByteChannel newByteChannel(byte[] path,
620 Set<? extends OpenOption> options,
621 FileAttribute<?>... attrs)
622 throws IOException
623 {
624 checkOptions(options);
625 if (options.contains(StandardOpenOption.WRITE) ||
626 options.contains(StandardOpenOption.APPEND)) {
627 checkWritable();
628 beginRead(); // only need a readlock, the "update()" will obtain
629 // thewritelock when the channel is closed
630 try {
631 ensureOpen();
632 Entry e = getEntry(path);
633 if (e != null) {
634 if (e.isDir() || options.contains(CREATE_NEW))
636 SeekableByteChannel sbc =
637 new EntryOutputChannel(new Entry(e, Entry.NEW));
638 if (options.contains(APPEND)) {
639 try (InputStream is = getInputStream(e)) { // copyover
640 byte[] buf = new byte[8192];
641 ByteBuffer bb = ByteBuffer.wrap(buf);
642 int n;
643 while ((n = is.read(buf)) != -1) {
644 bb.position(0);
645 bb.limit(n);
646 sbc.write(bb);
647 }
648 }
649 }
650 return sbc;
651 }
652 if (!options.contains(CREATE) && !options.contains(CREATE_NEW))
653 throw new NoSuchFileException(getString(path));
654 checkParents(path);
655 return new EntryOutputChannel(
656 new Entry(path, Entry.NEW, false, defaultMethod));
657
658 } finally {
659 endRead();
660 }
661 } else {
662 beginRead();
663 try {
664 ensureOpen();
665 Entry e = getEntry(path);
666 if (e == null || e.isDir())
667 throw new NoSuchFileException(getString(path));
668 try (InputStream is = getInputStream(e)) {
669 // TBD: if (e.size < NNNNN);
670 return new ByteArrayChannel(is.readAllBytes(), true);
671 }
672 } finally {
673 endRead();
674 }
675 }
676 }
704 throw new FileAlreadyExistsException(getString(path));
705 }
706 if (e.isDir())
707 throw new FileAlreadyExistsException("directory <"
708 + getString(path) + "> exists");
709 }
710 options = new HashSet<>(options);
711 options.remove(StandardOpenOption.CREATE_NEW); // for tmpfile
712 } else if (e == null || e.isDir()) {
713 throw new NoSuchFileException(getString(path));
714 }
715
716 final boolean isFCH = (e != null && e.type == Entry.FILECH);
717 final Path tmpfile = isFCH ? e.file : getTempPathForEntry(path);
718 final FileChannel fch = tmpfile.getFileSystem()
719 .provider()
720 .newFileChannel(tmpfile, options, attrs);
721 final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH);
722 if (forWrite) {
723 u.flag = FLAG_DATADESCR;
724 u.method = METHOD_DEFLATED;
725 }
726 // is there a better way to hook into the FileChannel's close method?
727 return new FileChannel() {
728 public int write(ByteBuffer src) throws IOException {
729 return fch.write(src);
730 }
731 public long write(ByteBuffer[] srcs, int offset, int length)
732 throws IOException
733 {
734 return fch.write(srcs, offset, length);
735 }
736 public long position() throws IOException {
737 return fch.position();
738 }
739 public FileChannel position(long newPosition)
740 throws IOException
741 {
742 fch.position(newPosition);
743 return this;
744 }
1390 public void write(int b) throws IOException {
1391 out.write(b);
1392 crc.update(b);
1393 written += 1;
1394 }
1395
1396 @Override
1397 public void write(byte b[], int off, int len)
1398 throws IOException {
1399 out.write(b, off, len);
1400 crc.update(b, off, len);
1401 written += len;
1402 }
1403
1404 @Override
1405 public void close() throws IOException {
1406 if (isClosed)
1407 return;
1408 isClosed = true;
1409 e.size = e.csize = written;
1410 e.size = crc.getValue();
1411 }
1412 }
1413
1414 // Wrapper output stream class to write out a "deflated" entry.
1415 // (1) this class does not close the underlying out stream when
1416 // being closed.
1417 // (2) no need to be "synchronized", only used by sync()
1418 private class EntryOutputStreamDef extends DeflaterOutputStream {
1419 private CRC32 crc;
1420 private Entry e;
1421 private boolean isClosed;
1422
1423 EntryOutputStreamDef(Entry e, OutputStream os) throws IOException {
1424 super(os, getDeflater());
1425 this.e = Objects.requireNonNull(e, "Zip entry is null");
1426 this.crc = new CRC32();
1427 }
1428
1429 @Override
1430 public void write(byte b[], int off, int len)
|
595 if (e.mtime == -1)
596 e.mtime = System.currentTimeMillis();
597 if (e.method == -1)
598 e.method = defaultMethod;
599 // store size, compressed size, and crc-32 in datadescriptor
600 e.flag = FLAG_DATADESCR;
601 if (zc.isUTF8())
602 e.flag |= FLAG_USE_UTF8;
603 }
604
605 @Override
606 public void close() throws IOException {
607 e.bytes = toByteArray();
608 e.size = e.bytes.length;
609 e.crc = -1;
610 super.close();
611 update(e);
612 }
613 }
614
615 private int getCompressMethod(FileAttribute<?>... attrs) {
616 return defaultMethod;
617 }
618
619 // Returns a Writable/ReadByteChannel for now. Might consdier to use
620 // newFileChannel() instead, which dump the entry data into a regular
621 // file on the default file system and create a FileChannel on top of
622 // it.
623 SeekableByteChannel newByteChannel(byte[] path,
624 Set<? extends OpenOption> options,
625 FileAttribute<?>... attrs)
626 throws IOException
627 {
628 checkOptions(options);
629 if (options.contains(StandardOpenOption.WRITE) ||
630 options.contains(StandardOpenOption.APPEND)) {
631 checkWritable();
632 beginRead(); // only need a readlock, the "update()" will obtain
633 // thewritelock when the channel is closed
634 try {
635 ensureOpen();
636 Entry e = getEntry(path);
637 if (e != null) {
638 if (e.isDir() || options.contains(CREATE_NEW))
640 SeekableByteChannel sbc =
641 new EntryOutputChannel(new Entry(e, Entry.NEW));
642 if (options.contains(APPEND)) {
643 try (InputStream is = getInputStream(e)) { // copyover
644 byte[] buf = new byte[8192];
645 ByteBuffer bb = ByteBuffer.wrap(buf);
646 int n;
647 while ((n = is.read(buf)) != -1) {
648 bb.position(0);
649 bb.limit(n);
650 sbc.write(bb);
651 }
652 }
653 }
654 return sbc;
655 }
656 if (!options.contains(CREATE) && !options.contains(CREATE_NEW))
657 throw new NoSuchFileException(getString(path));
658 checkParents(path);
659 return new EntryOutputChannel(
660 new Entry(path, Entry.NEW, false, getCompressMethod(attrs)));
661
662 } finally {
663 endRead();
664 }
665 } else {
666 beginRead();
667 try {
668 ensureOpen();
669 Entry e = getEntry(path);
670 if (e == null || e.isDir())
671 throw new NoSuchFileException(getString(path));
672 try (InputStream is = getInputStream(e)) {
673 // TBD: if (e.size < NNNNN);
674 return new ByteArrayChannel(is.readAllBytes(), true);
675 }
676 } finally {
677 endRead();
678 }
679 }
680 }
708 throw new FileAlreadyExistsException(getString(path));
709 }
710 if (e.isDir())
711 throw new FileAlreadyExistsException("directory <"
712 + getString(path) + "> exists");
713 }
714 options = new HashSet<>(options);
715 options.remove(StandardOpenOption.CREATE_NEW); // for tmpfile
716 } else if (e == null || e.isDir()) {
717 throw new NoSuchFileException(getString(path));
718 }
719
720 final boolean isFCH = (e != null && e.type == Entry.FILECH);
721 final Path tmpfile = isFCH ? e.file : getTempPathForEntry(path);
722 final FileChannel fch = tmpfile.getFileSystem()
723 .provider()
724 .newFileChannel(tmpfile, options, attrs);
725 final Entry u = isFCH ? e : new Entry(path, tmpfile, Entry.FILECH);
726 if (forWrite) {
727 u.flag = FLAG_DATADESCR;
728 u.method = getCompressMethod(attrs);
729 }
730 // is there a better way to hook into the FileChannel's close method?
731 return new FileChannel() {
732 public int write(ByteBuffer src) throws IOException {
733 return fch.write(src);
734 }
735 public long write(ByteBuffer[] srcs, int offset, int length)
736 throws IOException
737 {
738 return fch.write(srcs, offset, length);
739 }
740 public long position() throws IOException {
741 return fch.position();
742 }
743 public FileChannel position(long newPosition)
744 throws IOException
745 {
746 fch.position(newPosition);
747 return this;
748 }
1394 public void write(int b) throws IOException {
1395 out.write(b);
1396 crc.update(b);
1397 written += 1;
1398 }
1399
1400 @Override
1401 public void write(byte b[], int off, int len)
1402 throws IOException {
1403 out.write(b, off, len);
1404 crc.update(b, off, len);
1405 written += len;
1406 }
1407
1408 @Override
1409 public void close() throws IOException {
1410 if (isClosed)
1411 return;
1412 isClosed = true;
1413 e.size = e.csize = written;
1414 e.crc = crc.getValue();
1415 }
1416 }
1417
1418 // Wrapper output stream class to write out a "deflated" entry.
1419 // (1) this class does not close the underlying out stream when
1420 // being closed.
1421 // (2) no need to be "synchronized", only used by sync()
1422 private class EntryOutputStreamDef extends DeflaterOutputStream {
1423 private CRC32 crc;
1424 private Entry e;
1425 private boolean isClosed;
1426
1427 EntryOutputStreamDef(Entry e, OutputStream os) throws IOException {
1428 super(os, getDeflater());
1429 this.e = Objects.requireNonNull(e, "Zip entry is null");
1430 this.crc = new CRC32();
1431 }
1432
1433 @Override
1434 public void write(byte b[], int off, int len)
|