Skip to content

Commit f9d2eed

Browse files
committedFeb 7, 2025
close native resources
1 parent dbf7524 commit f9d2eed

File tree

4 files changed

+59
-46
lines changed

4 files changed

+59
-46
lines changed
 

‎README.md

+20-18
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ We reuse standard `java.util.zip.ZipOutputStream` and
1616
1. Collect all zip entries and their bytes for each input file in parallel.
1717
For each input file:
1818
- Get a `ByteArrayOutputStream` and a `ZipOutputStream` on top of it
19-
- Write an entry to a zip stream. Do not close it to avoid writing an unneeded central directory
19+
- Write an entry to the zip stream skipping the central directory using `SingleEntryZipOutputStream`.
2020
- Get the bytes from the byte stream
2121

2222
```java
@@ -25,33 +25,35 @@ We reuse standard `java.util.zip.ZipOutputStream` and
2525
// for each input file in parallel:
2626
var out = new ByteArrayOutputStream();
2727
var zipEntry = new ZipEntry(filePathRelativeToZipRoot);
28-
var zip = new ZipOutputStream(out);
29-
try (var fileStream = Files.newInputStream(filePath)) {
30-
zip.putNextEntry(zipEntry);
31-
fileStream.transferTo(zip);
32-
zip.closeEntry();
28+
try (var zip = new SingleEntryZipOutputStream(out)) {
29+
try (var fileStream = Files.newInputStream(filePath)) {
30+
zip.putNextEntry(zipEntry);
31+
fileStream.transferTo(zip);
32+
zip.closeEntry();
33+
}
3334
}
3435
zipEntries.put(zipEntry, out.toByteArray());
3536
```
3637

3738
2. Write all entries and bytes sequentially to a target zip file:
3839
- Get a `FileOutputStream` and a `ZipOutputStream` on top of it
39-
- Write bytes of all entries to a file stream updating zip stream state
40-
- Write the central directory by closing the zip stream
40+
- Write bytes of all entries to the file stream updating the zip stream state
41+
- Write the central directory by closing the zip stream as usual
4142

4243
```java
4344
try (var os = Files.newOutputStream(zipFile)) {
44-
var zip = new ZipOutputStream(os);
45-
var offset = 0L;
46-
for (Map.Entry<ZipEntry, byte[]> o : zipEntries.entrySet()) {
47-
var zipEntry = o.getKey();
48-
var bytes = o.getValue();
49-
zip.xEntries.add(new XEntry(zipEntry, offset)); // via reflection
50-
os.write(bytes);
51-
offset += bytes.length;
45+
try (var zip = new ZipOutputStream(os)) {
46+
var offset = 0L;
47+
for (Map.Entry<ZipEntry, byte[]> o : zipEntries.entrySet()) {
48+
var zipEntry = o.getKey();
49+
var bytes = o.getValue();
50+
zip.xEntries.add(new XEntry(zipEntry, offset)); // via reflection
51+
os.write(bytes);
52+
offset += bytes.length;
53+
}
54+
zip.offset = offset; // via reflection
55+
// write the central directory on close
5256
}
53-
zip.offset = offset; // via reflection
54-
zip.close();
5557
}
5658
```
5759

‎src/main/java/parallelZip/MainJava.java

+18-17
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,12 @@ private static void addZipEntryToMap(Path relativePath, Path path, ConcurrentHas
6464
var out = new ByteArrayOutputStream();
6565
var zipEntry = new ZipEntry(relativePath.toString());
6666
// skip the central directory writing by _not_ closing the zip stream
67-
var zip = new ZipOutputStream(out);
68-
try (var fileStream = new BufferedInputStream(Files.newInputStream(path))) {
69-
zip.putNextEntry(zipEntry);
70-
fileStream.transferTo(zip);
71-
zip.closeEntry();
67+
try (var zip = new SingleEntryZipOutputStream(out)) {
68+
try (var fileStream = new BufferedInputStream(Files.newInputStream(path))) {
69+
zip.putNextEntry(zipEntry);
70+
fileStream.transferTo(zip);
71+
zip.closeEntry();
72+
}
7273
}
7374
catch (IOException e) {
7475
throw new RuntimeException(e);
@@ -93,19 +94,19 @@ private static void addZipFileEntriesToMap(Path path, ConcurrentHashMap<ZipEntry
9394
private static void writeZipEntriesToZip(Path zipFile, List<Map.Entry<ZipEntry, byte[]>> list) throws Exception {
9495
// a zip is just its entries and a central directory at the end
9596
try (var os = new BufferedOutputStream(Files.newOutputStream(zipFile))) {
96-
var zip = new ZipOutputStream(os);
97-
var xEntries = (Vector<Object>)varEntries.get(zip);
98-
var offset = 0L;
99-
for (var item : list) {
100-
var zipEntry = item.getKey();
101-
var bytes = item.getValue();
102-
xEntries.add(xEntryConstructor.newInstance(zipEntry, offset));
103-
os.write(bytes);
104-
offset += bytes.length;
97+
try (var zip = new ZipOutputStream(os)) {
98+
var xEntries = (Vector<Object>)varEntries.get(zip);
99+
var offset = 0L;
100+
for (var item : list) {
101+
var zipEntry = item.getKey();
102+
var bytes = item.getValue();
103+
xEntries.add(xEntryConstructor.newInstance(zipEntry, offset));
104+
os.write(bytes);
105+
offset += bytes.length;
106+
}
107+
varWritten.set(zip, offset);
108+
// write the central directory on close
105109
}
106-
// write the central directory
107-
varWritten.set(zip, offset);
108-
zip.close();
109110
}
110111
}
111112

‎src/main/kotlin/parallelZip/MainKotlin.kt

+11-11
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ private fun addZipEntryToMap(relativePath: Path, path: Path, zipEntries: Concurr
5757
val out = ByteArrayOutputStream()
5858
val zipEntry = ZipEntry(relativePath.toString())
5959
// skip the central directory writing by _not_ closing the zip stream
60-
ZipOutputStream(out).let {
60+
SingleEntryZipOutputStream(out).use {
6161
it.putNextEntry(zipEntry)
6262
path.inputStream().buffered().use { s -> s.copyTo(it) }
6363
it.closeEntry()
@@ -77,17 +77,17 @@ private fun addZipFileEntriesToMap(path: Path, zipEntries: ConcurrentHashMap<Zip
7777
private fun writeZipEntriesToZip(zipFile: Path, list: List<Map.Entry<ZipEntry, ByteArray>>) {
7878
// a zip is just its entries and a central directory at the end
7979
zipFile.outputStream().buffered().use { os ->
80-
val zip = ZipOutputStream(os)
81-
val xEntries = varEntries.get(zip) as Vector<Any>
82-
var offset = 0L
83-
list.forEach { (zipEntry, bytes) ->
84-
xEntries.add(xEntryConstructor.newInstance(zipEntry, offset))
85-
os.write(bytes)
86-
offset += bytes.size.toLong()
80+
ZipOutputStream(os).use { zip ->
81+
val xEntries = varEntries.get(zip) as Vector<Any>
82+
var offset = 0L
83+
list.forEach { (zipEntry, bytes) ->
84+
xEntries.add(xEntryConstructor.newInstance(zipEntry, offset))
85+
os.write(bytes)
86+
offset += bytes.size.toLong()
87+
}
88+
varWritten.set(zip, offset)
89+
// write the central directory on close
8790
}
88-
// write the central directory
89-
varWritten.set(zip, offset)
90-
zip.close()
9191
}
9292
}
9393

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package parallelZip
2+
3+
import java.io.OutputStream
4+
import java.util.zip.ZipOutputStream
5+
6+
class SingleEntryZipOutputStream(out: OutputStream) : ZipOutputStream(out) {
7+
override fun finish() {
8+
closeEntry()
9+
}
10+
}

0 commit comments

Comments
 (0)
Please sign in to comment.