|
37 | 37 | * this sample code. |
38 | 38 | */ |
39 | 39 |
|
40 | | - |
41 | 40 | package com.sun.nio.zipfs; |
42 | 41 |
|
43 | 42 | import java.io.BufferedOutputStream; |
@@ -94,6 +93,10 @@ public class ZipFileSystem extends FileSystem { |
94 | 93 | private static final boolean isWindows = |
95 | 94 | System.getProperty("os.name").startsWith("Windows"); |
96 | 95 |
|
| 96 | + // a threshold, in bytes, to decide whether to create a temp file |
| 97 | + // for outputstream of a zip entry |
| 98 | + private final int tempFileCreationThreshold = 10 * 1024 * 1024; // 10 MB |
| 99 | + |
97 | 100 | ZipFileSystem(ZipFileSystemProvider provider, |
98 | 101 | Path zfpath, |
99 | 102 | Map<String, ?> env) |
@@ -1417,15 +1420,120 @@ private OutputStream getOutputStream(Entry e) throws IOException { |
1417 | 1420 | if (zc.isUTF8()) |
1418 | 1421 | e.flag |= FLAG_EFS; |
1419 | 1422 | OutputStream os; |
1420 | | - if (useTempFile) { |
| 1423 | + if (useTempFile || e.size >= tempFileCreationThreshold) { |
1421 | 1424 | e.file = getTempPathForEntry(null); |
1422 | 1425 | os = Files.newOutputStream(e.file, WRITE); |
1423 | 1426 | } else { |
1424 | | - os = new ByteArrayOutputStream((e.size > 0)? (int)e.size : 8192); |
| 1427 | + os = new FileRolloverOutputStream(e); |
1425 | 1428 | } |
1426 | 1429 | return new EntryOutputStream(e, os); |
1427 | 1430 | } |
1428 | 1431 |
|
| 1432 | + // A wrapper around the ByteArrayOutputStream. This FileRolloverOutputStream |
| 1433 | + // uses a threshold size to decide if the contents being written need to be |
| 1434 | + // rolled over into a temporary file. Until the threshold is reached, writes |
| 1435 | + // on this outputstream just write it to the internal in-memory byte array |
| 1436 | + // held by the ByteArrayOutputStream. Once the threshold is reached, the |
| 1437 | + // write operation on this outputstream first (and only once) creates a temporary file |
| 1438 | + // and transfers the data that has so far been written in the internal |
| 1439 | + // byte array, to that newly created file. The temp file is then opened |
| 1440 | + // in append mode and any subsequent writes, including the one which triggered |
| 1441 | + // the temporary file creation, will be written to the file. |
| 1442 | + // Implementation note: the "write" and the "close" methods of this implementation |
| 1443 | + // aren't "synchronized" because this FileRolloverOutputStream gets called |
| 1444 | + // only from either DeflatingEntryOutputStream or EntryOutputStream, both of which |
| 1445 | + // already have the necessary "synchronized" before calling these methods. |
| 1446 | + private class FileRolloverOutputStream extends OutputStream { |
| 1447 | + private ByteArrayOutputStream baos = new ByteArrayOutputStream(8192); |
| 1448 | + private final Entry entry; |
| 1449 | + private OutputStream tmpFileOS; |
| 1450 | + private long totalWritten = 0; |
| 1451 | + |
| 1452 | + private FileRolloverOutputStream(final Entry e) { |
| 1453 | + this.entry = e; |
| 1454 | + } |
| 1455 | + |
| 1456 | + @Override |
| 1457 | + public void write(final int b) throws IOException { |
| 1458 | + if (tmpFileOS != null) { |
| 1459 | + // already rolled over, write to the file that has been created previously |
| 1460 | + writeToFile(b); |
| 1461 | + return; |
| 1462 | + } |
| 1463 | + if (totalWritten + 1 < tempFileCreationThreshold) { |
| 1464 | + // write to our in-memory byte array |
| 1465 | + baos.write(b); |
| 1466 | + totalWritten++; |
| 1467 | + return; |
| 1468 | + } |
| 1469 | + // rollover into a file |
| 1470 | + transferToFile(); |
| 1471 | + writeToFile(b); |
| 1472 | + } |
| 1473 | + |
| 1474 | + @Override |
| 1475 | + public void write(final byte[] b) throws IOException { |
| 1476 | + write(b, 0, b.length); |
| 1477 | + } |
| 1478 | + |
| 1479 | + @Override |
| 1480 | + public void write(final byte[] b, final int off, final int len) throws IOException { |
| 1481 | + if (tmpFileOS != null) { |
| 1482 | + // already rolled over, write to the file that has been created previously |
| 1483 | + writeToFile(b, off, len); |
| 1484 | + return; |
| 1485 | + } |
| 1486 | + if (totalWritten + len < tempFileCreationThreshold) { |
| 1487 | + // write to our in-memory byte array |
| 1488 | + baos.write(b, off, len); |
| 1489 | + totalWritten += len; |
| 1490 | + return; |
| 1491 | + } |
| 1492 | + // rollover into a file |
| 1493 | + transferToFile(); |
| 1494 | + writeToFile(b, off, len); |
| 1495 | + } |
| 1496 | + |
| 1497 | + @Override |
| 1498 | + public void flush() throws IOException { |
| 1499 | + if (tmpFileOS != null) { |
| 1500 | + tmpFileOS.flush(); |
| 1501 | + } |
| 1502 | + } |
| 1503 | + |
| 1504 | + @Override |
| 1505 | + public void close() throws IOException { |
| 1506 | + baos = null; |
| 1507 | + if (tmpFileOS != null) { |
| 1508 | + tmpFileOS.close(); |
| 1509 | + } |
| 1510 | + } |
| 1511 | + |
| 1512 | + private void writeToFile(int b) throws IOException { |
| 1513 | + tmpFileOS.write(b); |
| 1514 | + totalWritten++; |
| 1515 | + } |
| 1516 | + |
| 1517 | + private void writeToFile(byte[] b, int off, int len) throws IOException { |
| 1518 | + tmpFileOS.write(b, off, len); |
| 1519 | + totalWritten += len; |
| 1520 | + } |
| 1521 | + |
| 1522 | + private void transferToFile() throws IOException { |
| 1523 | + // create a tempfile |
| 1524 | + entry.file = getTempPathForEntry(null); |
| 1525 | + tmpFileOS = new BufferedOutputStream(Files.newOutputStream(entry.file)); |
| 1526 | + // transfer the already written data from the byte array buffer into this tempfile |
| 1527 | + baos.writeTo(tmpFileOS); |
| 1528 | + // release the underlying byte array |
| 1529 | + baos = null; |
| 1530 | + } |
| 1531 | + |
| 1532 | + private byte[] toByteArray() { |
| 1533 | + return baos == null ? null : baos.toByteArray(); |
| 1534 | + } |
| 1535 | + } |
| 1536 | + |
1429 | 1537 | private InputStream getInputStream(Entry e) |
1430 | 1538 | throws IOException |
1431 | 1539 | { |
@@ -1644,8 +1752,12 @@ public synchronized void close() throws IOException { |
1644 | 1752 | throw new ZipException("invalid compression method"); |
1645 | 1753 | } |
1646 | 1754 | //crc.reset(); |
1647 | | - if (out instanceof ByteArrayOutputStream) |
1648 | | - e.bytes = ((ByteArrayOutputStream)out).toByteArray(); |
| 1755 | + if (out instanceof FileRolloverOutputStream) { |
| 1756 | + FileRolloverOutputStream fros = (FileRolloverOutputStream) out; |
| 1757 | + if (fros.tmpFileOS == null) { |
| 1758 | + e.bytes = fros.toByteArray(); |
| 1759 | + } |
| 1760 | + } |
1649 | 1761 |
|
1650 | 1762 | if (e.type == Entry.FILECH) { |
1651 | 1763 | releaseDeflater(def); |
|
0 commit comments