23
23
import java .io .InputStream ;
24
24
import java .io .OutputStream ;
25
25
import java .net .URI ;
26
- import java .nio .charset .StandardCharsets ;
27
26
import java .nio .file .CopyOption ;
28
- import java .nio .file .DirectoryStream ;
27
+ import java .nio .file .DirectoryStream . Filter ;
29
28
import java .nio .file .FileSystem ;
30
29
import java .nio .file .FileSystems ;
31
- import java .nio .file .FileVisitResult ;
32
30
import java .nio .file .Files ;
33
- import java .nio .file .LinkOption ;
34
31
import java .nio .file .Path ;
35
- import java .nio .file .SimpleFileVisitor ;
36
32
import java .nio .file .StandardCopyOption ;
37
- import java .nio .file .StandardOpenOption ;
38
- import java .nio .file .attribute .BasicFileAttributes ;
39
33
import java .util .Map ;
40
34
import java .util .UUID ;
41
35
import java .util .function .BiConsumer ;
42
- import java .util .function .Predicate ;
43
- import java .util .zip .ZipEntry ;
44
- import java .util .zip .ZipInputStream ;
45
- import java .util .zip .ZipOutputStream ;
46
36
import lombok .NonNull ;
47
37
import org .jetbrains .annotations .ApiStatus .Internal ;
48
38
import org .jetbrains .annotations .Nullable ;
49
39
50
- /**
51
- * The FileUtils class has a lot of utility methods, for
52
- * <ol>
53
- * <li>Byte Streams IO</li>
54
- * <li>File IO (Coping, Deleting)</li>
55
- * <li>Zip IO</li>
56
- * </ol>
57
- */
58
40
@ Internal
59
41
public final class FileUtil {
60
42
61
43
public static final Path TEMP_DIR = Path .of (System .getProperty ("cloudnet.tempDir" , "temp" ));
62
44
63
45
private static final Logger LOGGER = LogManager .logger (FileUtil .class );
64
- private static final DirectoryStream .Filter <Path > ACCEPTING_FILTER = $ -> true ;
65
- private static final boolean IS_WINDOWS = System .getProperty ("os.name" ).contains ("windows" );
66
-
67
- private static final Map <String , String > ZIP_FILE_SYSTEM_PROPERTIES = Map .of (
68
- "create" , "false" , "encoding" , "UTF-8" );
46
+ private static final Filter <Path > ACCEPTING_FILTER = $ -> true ;
47
+ private static final Map <String , String > ZIP_FILE_SYSTEM_PROPERTIES = Map .of ("create" , "false" , "encoding" , "UTF-8" );
69
48
70
49
private FileUtil () {
71
50
throw new UnsupportedOperationException ();
72
51
}
73
52
74
- public static void openZipFileSystem (@ NonNull Path zip , @ NonNull ThrowableConsumer <FileSystem , Exception > consumer ) {
75
- try (var fs = FileSystems .newFileSystem (URI .create ("jar:" + zip .toUri ()), ZIP_FILE_SYSTEM_PROPERTIES )) {
53
+ public static void openJarFileSystem (@ NonNull Path jar , @ NonNull ThrowableConsumer <FileSystem , Exception > consumer ) {
54
+ try (var fs = FileSystems .newFileSystem (URI .create ("jar:" + jar .toUri ()), ZIP_FILE_SYSTEM_PROPERTIES )) {
76
55
consumer .accept (fs );
77
56
} catch (Exception throwable ) {
78
- LOGGER .severe ("Exception while opening file" , throwable );
57
+ LOGGER .severe ("Exception opening jar file system on %s " , throwable , jar );
79
58
}
80
59
}
81
60
82
61
public static void move (@ NonNull Path from , @ NonNull Path to , CopyOption @ NonNull ... options ) {
83
62
try {
84
63
Files .move (from , to , options );
85
64
} catch (IOException exception ) {
86
- LOGGER .severe ("Unable to move file " + from + " to " + to , exception );
65
+ LOGGER .severe ("Exception moving file from %s to %s" , exception , from , to );
87
66
}
88
67
}
89
68
@@ -92,18 +71,18 @@ public static void copy(@Nullable InputStream inputStream, @Nullable OutputStrea
92
71
try {
93
72
inputStream .transferTo (outputStream );
94
73
} catch (IOException exception ) {
95
- LOGGER .severe ("Exception copying InputStream to OutputStream " , exception );
74
+ LOGGER .severe ("Exception copying input stream to output stream " , exception );
96
75
}
97
76
}
98
77
}
99
78
100
79
public static void copy (@ Nullable InputStream inputStream , @ Nullable Path target ) {
101
80
if (inputStream != null && target != null ) {
102
- FileUtil . createDirectory (target .getParent ());
81
+ createDirectory (target .getParent ());
103
82
try (var out = Files .newOutputStream (target )) {
104
83
FileUtil .copy (inputStream , out );
105
84
} catch (IOException exception ) {
106
- LOGGER .severe ("Exception copying InputStream to Path " , exception );
85
+ LOGGER .severe ("Exception copying input stream to %s " , exception , target );
107
86
}
108
87
}
109
88
}
@@ -114,15 +93,15 @@ public static void copy(@NonNull Path from, @NonNull Path to) {
114
93
createDirectory (to .getParent ());
115
94
Files .copy (from , to , StandardCopyOption .REPLACE_EXISTING );
116
95
} catch (IOException exception ) {
117
- LOGGER .severe ("Exception copying file from " + from + " to " + to , exception );
96
+ LOGGER .severe ("Exception copying file from %s to %s" , exception , from , to );
118
97
}
119
98
}
120
99
121
100
public static void copyDirectory (@ NonNull Path from , @ NonNull Path to ) {
122
101
copyDirectory (from , to , null );
123
102
}
124
103
125
- public static void copyDirectory (Path from , Path to , DirectoryStream . Filter <Path > filter ) {
104
+ public static void copyDirectory (@ NonNull Path from , @ NonNull Path to , @ Nullable Filter <Path > filter ) {
126
105
walkFileTree (from , ($ , current ) -> {
127
106
if (!Files .isDirectory (current )) {
128
107
FileUtil .copy (current , to .resolve (from .relativize (current )));
@@ -136,8 +115,9 @@ public static void delete(@Nullable Path path) {
136
115
if (Files .isDirectory (path )) {
137
116
walkFileTree (path , ($ , current ) -> FileUtil .delete (current ));
138
117
}
139
- // remove the directory or the file
118
+
140
119
try {
120
+ // remove the directory or the file
141
121
Files .delete (path );
142
122
} catch (IOException ignored ) {
143
123
// ignore these exceptions
@@ -146,124 +126,10 @@ public static void delete(@Nullable Path path) {
146
126
}
147
127
148
128
public static @ NonNull Path createTempFile () {
149
- if (Files .notExists (TEMP_DIR )) {
150
- createDirectory (TEMP_DIR );
151
- }
152
-
129
+ createDirectory (TEMP_DIR );
153
130
return TEMP_DIR .resolve (UUID .randomUUID ().toString ());
154
131
}
155
132
156
- public static @ NonNull InputStream zipToStream (@ NonNull Path directory ) {
157
- return zipToStream (directory , null );
158
- }
159
-
160
- public static @ NonNull InputStream zipToStream (@ NonNull Path directory , @ Nullable Predicate <Path > fileFilter ) {
161
- var target = createTempFile ();
162
- zipToFile (
163
- directory ,
164
- target ,
165
- path -> !target .equals (path ) && (fileFilter == null || fileFilter .test (path )));
166
-
167
- try {
168
- return Files .newInputStream (target , StandardOpenOption .DELETE_ON_CLOSE , LinkOption .NOFOLLOW_LINKS );
169
- } catch (IOException exception ) {
170
- throw new IllegalStateException ("Unable to open input stream to zip file " + target , exception );
171
- }
172
- }
173
-
174
- public static @ Nullable Path zipToFile (@ NonNull Path directory , @ NonNull Path target ) {
175
- return zipToFile (directory , target , null );
176
- }
177
-
178
- public static @ Nullable Path zipToFile (@ NonNull Path dir , @ NonNull Path target , @ Nullable Predicate <Path > filter ) {
179
- if (Files .exists (dir )) {
180
- try (var out = new ZipOutputStream (Files .newOutputStream (target ), StandardCharsets .UTF_8 )) {
181
- zipDir (out , dir , filter );
182
- return target ;
183
- } catch (IOException exception ) {
184
- LOGGER .severe ("Exception while processing new zip entry from directory " + dir , exception );
185
- }
186
- }
187
-
188
- return null ;
189
- }
190
-
191
- private static void zipDir (
192
- @ NonNull ZipOutputStream out ,
193
- @ NonNull Path dir ,
194
- @ Nullable Predicate <Path > filter
195
- ) throws IOException {
196
- Files .walkFileTree (
197
- dir ,
198
- new SimpleFileVisitor <>() {
199
- @ Override
200
- public FileVisitResult visitFile (@ NonNull Path file , @ NonNull BasicFileAttributes attrs ) throws IOException {
201
- if (filter == null || filter .test (file )) {
202
- try {
203
- out .putNextEntry (new ZipEntry (dir .relativize (file ).toString ().replace ("\\ " , "/" )));
204
- Files .copy (file , out );
205
- } finally {
206
- out .closeEntry ();
207
- }
208
- }
209
- // continue search
210
- return FileVisitResult .CONTINUE ;
211
- }
212
- }
213
- );
214
- }
215
-
216
- public static @ Nullable Path extract (@ NonNull Path zipPath , @ NonNull Path targetDirectory ) {
217
- if (Files .exists (zipPath )) {
218
- try (var inputStream = Files .newInputStream (zipPath )) {
219
- return extract (inputStream , targetDirectory );
220
- } catch (IOException exception ) {
221
- LOGGER .severe ("Unable to extract zip from " + zipPath + " to " + targetDirectory , exception );
222
- }
223
- }
224
- return null ;
225
- }
226
-
227
- public static @ Nullable Path extract (@ NonNull InputStream in , @ NonNull Path targetDirectory ) {
228
- return extractZipStream (
229
- in instanceof ZipInputStream ? (ZipInputStream ) in : new ZipInputStream (in , StandardCharsets .UTF_8 ),
230
- targetDirectory );
231
- }
232
-
233
- public static @ Nullable Path extractZipStream (@ NonNull ZipInputStream zipInputStream , @ NonNull Path targetDirectory ) {
234
- try {
235
- ZipEntry zipEntry ;
236
- while ((zipEntry = zipInputStream .getNextEntry ()) != null ) {
237
- extractEntry (zipInputStream , zipEntry , targetDirectory );
238
- zipInputStream .closeEntry ();
239
- }
240
-
241
- return targetDirectory ;
242
- } catch (IOException exception ) {
243
- LOGGER .severe ("Exception unzipping zip file to " + targetDirectory , exception );
244
- return null ;
245
- }
246
- }
247
-
248
- private static void extractEntry (
249
- @ NonNull ZipInputStream in ,
250
- @ NonNull ZipEntry zipEntry ,
251
- @ NonNull Path targetDirectory
252
- ) throws IOException {
253
- // checks first if the zip entry name is malicious before extracting
254
- ensureSafeZipEntryName (zipEntry .getName ());
255
- var file = targetDirectory .resolve (zipEntry .getName ());
256
-
257
- if (zipEntry .isDirectory ()) {
258
- FileUtil .createDirectory (file );
259
- } else {
260
- FileUtil .createDirectory (file .getParent ());
261
- try (var outputStream = Files .newOutputStream (file )) {
262
- copy (in , outputStream );
263
- }
264
- }
265
- }
266
-
267
133
public static void walkFileTree (@ NonNull Path root , @ NonNull BiConsumer <Path , Path > consumer ) {
268
134
walkFileTree (root , consumer , true );
269
135
}
@@ -289,7 +155,7 @@ public static void walkFileTree(
289
155
@ NonNull Path root ,
290
156
@ NonNull BiConsumer <Path , Path > consumer ,
291
157
boolean visitDirectories ,
292
- @ NonNull DirectoryStream . Filter <Path > filter
158
+ @ NonNull Filter <Path > filter
293
159
) {
294
160
if (Files .exists (root )) {
295
161
try (var stream = Files .newDirectoryStream (root , filter )) {
@@ -300,14 +166,14 @@ public static void walkFileTree(
300
166
if (visitDirectories ) {
301
167
walkFileTree (path , consumer , true , filter );
302
168
} else {
303
- return ;
169
+ continue ;
304
170
}
305
171
}
306
172
// accepts all files and directories
307
173
consumer .accept (root , path );
308
174
}
309
175
} catch (IOException exception ) {
310
- LOGGER .severe ("Exception walking directory tree from " + root , exception );
176
+ LOGGER .severe ("Exception walking down directory tree starting at %s" , exception , root );
311
177
}
312
178
}
313
179
}
@@ -317,7 +183,7 @@ public static void createDirectory(@Nullable Path directoryPath) {
317
183
try {
318
184
Files .createDirectories (directoryPath );
319
185
} catch (IOException exception ) {
320
- LOGGER .severe ("Exception while creating directory" , exception );
186
+ LOGGER .severe ("Exception creating directory at %s " , exception , directoryPath );
321
187
}
322
188
}
323
189
}
@@ -331,16 +197,6 @@ public static void ensureChild(@NonNull Path root, @NonNull Path child) {
331
197
}
332
198
}
333
199
334
- public static void ensureSafeZipEntryName (@ NonNull String name ) {
335
- if (name .isEmpty ()
336
- || name .startsWith ("/" )
337
- || name .startsWith ("\\ " )
338
- || name .contains (".." )
339
- || (name .contains (":" ) && IS_WINDOWS )) {
340
- throw new IllegalStateException (String .format ("zip entry name %s contains unsafe characters" , name ));
341
- }
342
- }
343
-
344
200
public static @ NonNull Path resolve (@ NonNull Path base , String @ NonNull ... more ) {
345
201
for (var child : more ) {
346
202
base = base .resolve (child );
0 commit comments