Skip to content

Commit fabaec0

Browse files
authored
MFileZip fixes (#1345)
* Simplify code * Fix length and last modified for root zip file and add unit tests for this * Add isZipFile to MFile, to distinguish between directories and zip doriectories * Handle MFileZip writeToStream as MFileOS would * Allow zip extension to be case insensive * Close zip files in tests * Fix expected zip file length in tests * Rename variables and add name to temporary file * Add test for writeToStream with offset and size
1 parent 004f75d commit fabaec0

3 files changed

Lines changed: 84 additions & 22 deletions

File tree

cdm/core/src/main/java/thredds/inventory/MFile.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ public interface MFile extends Comparable<MFile> {
3535

3636
boolean isDirectory();
3737

38+
/**
39+
* Check if this MFile is a zip file
40+
*
41+
* @return true if the MFile is a zip file
42+
*/
43+
default boolean isZipFile() {
44+
return false;
45+
}
46+
3847
default boolean isReadable() {
3948
return true;
4049
}

cdm/zarr/src/main/java/thredds/inventory/zarr/MFileZip.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.io.InputStream;
99
import java.io.OutputStream;
10+
import java.util.Locale;
1011
import javax.annotation.Nonnull;
1112
import thredds.filesystem.MFileOS;
1213
import thredds.inventory.MFile;
@@ -22,6 +23,7 @@
2223
import java.util.zip.ZipFile;
2324
import java.util.zip.ZipInputStream;
2425
import ucar.nc2.util.IO;
26+
import ucar.unidata.io.RandomAccessFile;
2527

2628
/**
2729
* Implements thredds.inventory.MFile for ZipFiles and ZipEntries
@@ -101,23 +103,28 @@ private List<ZipEntry> getEntries() {
101103

102104
@Override
103105
public long getLastModified() {
104-
return this.entry == null ? 0 : this.entry.getLastModifiedTime().toMillis();
106+
return this.entry == null ? rootPath.toFile().lastModified() : this.entry.getLastModifiedTime().toMillis();
105107
}
106108

107109
@Override
108110
public long getLength() {
109-
return this.entry == null ? 0 : this.entry.getSize();
111+
return this.entry == null ? rootPath.toFile().length() : this.entry.getSize();
110112
}
111113

112114
@Override
113115
public boolean isDirectory() {
114116
return leafEntries.size() > 1;
115117
}
116118

119+
@Override
120+
public boolean isZipFile() {
121+
return true;
122+
}
123+
117124
@Override
118125
public boolean isReadable() {
119126
// readable if root is readable
120-
return Files.isReadable(Paths.get(root.getName()));
127+
return Files.isReadable(rootPath);
121128
}
122129

123130
@Override
@@ -162,16 +169,14 @@ public InputStream getInputStream() {
162169

163170
@Override
164171
public void writeToStream(OutputStream outputStream) throws IOException {
165-
for (ZipEntry entry : leafEntries) {
166-
final File file = new File(entry.getName());
167-
IO.copyFile(file, outputStream);
168-
}
172+
IO.copyFile(rootPath.toFile(), outputStream);
169173
}
170174

171175
@Override
172176
public void writeToStream(OutputStream outputStream, long offset, long maxBytes) throws IOException {
173-
throw new UnsupportedOperationException(
174-
"Writing MFileZip with a byte range to stream not implemented. Filename: " + getName());
177+
try (RandomAccessFile randomAccessFile = RandomAccessFile.acquire(rootPath.toString())) {
178+
IO.copyRafB(randomAccessFile, offset, maxBytes, outputStream);
179+
}
175180
}
176181

177182
@Override
@@ -202,7 +207,7 @@ public String getProtocol() {
202207

203208
@Override
204209
public boolean canProvide(String location) {
205-
return location != null && location.contains(ext);
210+
return location != null && location.toLowerCase(Locale.ROOT).contains(ext);
206211
}
207212

208213
@Nonnull

cdm/zarr/src/test/java/thredds/inventory/zarr/TestMFileZip.java

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,33 +40,81 @@ public static List<Integer[]> getTestParameters() {
4040
}
4141

4242
@Parameterized.Parameter(0)
43-
public int expectedSize;
43+
public int entrySize;
4444

4545
@Parameterized.Parameter(1)
46-
public int expectedNumberOfFiles;
46+
public int numberOfEntries;
4747

4848
@Test
4949
public void shouldWriteZipToStream() throws IOException {
50-
final ZipFile zipFile = createTemporaryZipFile(expectedSize, expectedNumberOfFiles);
51-
final MFileZip mFile = new MFileZip(zipFile.getName());
50+
try (ZipFile zipFile = createTemporaryZipFile("TestWriteZip", entrySize, numberOfEntries)) {
51+
final MFileZip mFile = new MFileZip(zipFile.getName());
5252

53-
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
54-
mFile.writeToStream(outputStream);
55-
assertThat(outputStream.size()).isEqualTo(expectedSize * expectedNumberOfFiles);
53+
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
54+
mFile.writeToStream(outputStream);
55+
assertThat(outputStream.size()).isEqualTo(mFile.getLength());
56+
}
57+
}
58+
59+
@Test
60+
public void shouldWritePartialZipToStream() throws IOException {
61+
try (ZipFile zipFile = createTemporaryZipFile("TestWritePartialZip", entrySize, numberOfEntries)) {
62+
final MFileZip mFile = new MFileZip(zipFile.getName());
63+
final int length = (int) mFile.getLength();
64+
65+
final int offset = 1;
66+
final int maxBytes = 100;
67+
68+
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
69+
70+
final int startPosition = Math.min(offset, length);
71+
final int endPosition = Math.min(offset + maxBytes, length);
72+
73+
mFile.writeToStream(outputStream, offset, maxBytes);
74+
assertThat(outputStream.size()).isEqualTo(Math.max(0, endPosition - startPosition));
75+
}
5676
}
5777
}
5878

5979
public static class TestMFileZipNonParameterized {
6080
@Test
6181
public void shouldReturnTrueForExistingFile() throws IOException {
62-
final ZipFile zipFile = createTemporaryZipFile(2, 0);
63-
final MFileZip mFile = new MFileZip(zipFile.getName());
64-
assertThat(mFile.exists()).isEqualTo(true);
82+
try (ZipFile zipFile = createTemporaryZipFile("TestExists", 2, 0)) {
83+
final MFileZip mFile = new MFileZip(zipFile.getName());
84+
assertThat(mFile.exists()).isEqualTo(true);
85+
}
86+
}
87+
88+
@Test
89+
public void shouldGetLastModified() throws IOException {
90+
try (ZipFile zipFile = createTemporaryZipFile("TestLastModified", 20, 1)) {
91+
final MFileZip mFile = new MFileZip(zipFile.getName());
92+
assertThat(mFile.getLastModified()).isGreaterThan(0);
93+
assertThat(mFile.getLastModified()).isEqualTo(new File(zipFile.getName()).lastModified());
94+
}
95+
}
96+
97+
@Test
98+
public void shouldGetLengthForZipFile() throws IOException {
99+
try (ZipFile zipFile = createTemporaryZipFile("TestLength", 30, 1)) {
100+
final MFileZip mFile = new MFileZip(zipFile.getName());
101+
assertThat(mFile.getLength()).isGreaterThan(30);
102+
assertThat(mFile.getLength()).isEqualTo(new File(zipFile.getName()).length());
103+
}
104+
}
105+
106+
@Test
107+
public void shouldProvideForZipExtensions() {
108+
MFileZip.Provider provider = new MFileZip.Provider();
109+
assertThat(provider.canProvide("foo.zip")).isTrue();
110+
assertThat(provider.canProvide("foo.ZIP")).isTrue();
111+
assertThat(provider.canProvide("foo.zip2")).isTrue();
112+
assertThat(provider.canProvide("foo.txt")).isFalse();
65113
}
66114
}
67115

68-
private static ZipFile createTemporaryZipFile(int size, int numberOfFiles) throws IOException {
69-
final File zipFile = tempFolder.newFile("TestMFileZip" + size + "-" + numberOfFiles + ".zip");
116+
private static ZipFile createTemporaryZipFile(String name, int size, int numberOfFiles) throws IOException {
117+
final File zipFile = tempFolder.newFile(name + "-" + size + "-" + numberOfFiles + ".zip");
70118

71119
try (FileOutputStream fos = new FileOutputStream(zipFile.getPath());
72120
ZipOutputStream zipOS = new ZipOutputStream(fos)) {

0 commit comments

Comments
 (0)