|
|
|
@ -18,6 +18,11 @@ package org.springframework.util; |
|
|
|
|
|
|
|
|
|
|
|
import java.io.File; |
|
|
|
import java.io.File; |
|
|
|
import java.io.IOException; |
|
|
|
import java.io.IOException; |
|
|
|
|
|
|
|
import java.nio.file.FileVisitResult; |
|
|
|
|
|
|
|
import java.nio.file.Files; |
|
|
|
|
|
|
|
import java.nio.file.Path; |
|
|
|
|
|
|
|
import java.nio.file.SimpleFileVisitor; |
|
|
|
|
|
|
|
import java.nio.file.attribute.BasicFileAttributes; |
|
|
|
|
|
|
|
|
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
|
|
|
|
|
|
|
|
@ -27,29 +32,60 @@ import org.springframework.lang.Nullable; |
|
|
|
* @author Rob Harrop |
|
|
|
* @author Rob Harrop |
|
|
|
* @author Juergen Hoeller |
|
|
|
* @author Juergen Hoeller |
|
|
|
* @since 2.5.3 |
|
|
|
* @since 2.5.3 |
|
|
|
* @deprecated as of Spring Framework 5.0, in favor of native NIO API usage |
|
|
|
* @see java.io.File |
|
|
|
|
|
|
|
* @see java.nio.file.Path |
|
|
|
|
|
|
|
* @see java.nio.file.Files |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Deprecated |
|
|
|
|
|
|
|
public abstract class FileSystemUtils { |
|
|
|
public abstract class FileSystemUtils { |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Delete the supplied {@link File} - for directories, |
|
|
|
* Delete the supplied {@link File} - for directories, |
|
|
|
* recursively delete any nested directories or files as well. |
|
|
|
* recursively delete any nested directories or files as well. |
|
|
|
|
|
|
|
* <p>Note: Like {@link File#delete()}, this method does not throw any |
|
|
|
|
|
|
|
* exception but rather silently returns {@code false} in case of I/O |
|
|
|
|
|
|
|
* errors. Consider using {@link #deleteRecursively(Path)} for NIO-style |
|
|
|
|
|
|
|
* handling of I/O errors, clearly differentiating between non-existence |
|
|
|
|
|
|
|
* and failure to delete an existing file. |
|
|
|
* @param root the root {@code File} to delete |
|
|
|
* @param root the root {@code File} to delete |
|
|
|
* @return {@code true} if the {@code File} was deleted, |
|
|
|
* @return {@code true} if the {@code File} was successfully deleted, |
|
|
|
* otherwise {@code false} |
|
|
|
* otherwise {@code false} |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static boolean deleteRecursively(@Nullable File root) { |
|
|
|
public static boolean deleteRecursively(@Nullable File root) { |
|
|
|
if (root != null && root.exists()) { |
|
|
|
if (root != null) { |
|
|
|
if (root.isDirectory()) { |
|
|
|
try { |
|
|
|
File[] children = root.listFiles(); |
|
|
|
return deleteRecursively(root.toPath()); |
|
|
|
if (children != null) { |
|
|
|
} |
|
|
|
for (File child : children) { |
|
|
|
catch (IOException ex) { |
|
|
|
deleteRecursively(child); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
return root.delete(); |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Delete the supplied {@link File} - for directories, |
|
|
|
|
|
|
|
* recursively delete any nested directories or files as well. |
|
|
|
|
|
|
|
* @param root the root {@code File} to delete |
|
|
|
|
|
|
|
* @return {@code true} if the {@code File} existed and was deleted, |
|
|
|
|
|
|
|
* or {@code false} it it did not exist |
|
|
|
|
|
|
|
* @throws IOException in the case of I/O errors |
|
|
|
|
|
|
|
* @since 5.0 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public static boolean deleteRecursively(@Nullable Path root) throws IOException { |
|
|
|
|
|
|
|
if (root != null) { |
|
|
|
|
|
|
|
Files.walkFileTree(root, new SimpleFileVisitor<Path>() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { |
|
|
|
|
|
|
|
Files.delete(file); |
|
|
|
|
|
|
|
return FileVisitResult.CONTINUE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { |
|
|
|
|
|
|
|
Files.delete(dir); |
|
|
|
|
|
|
|
return FileVisitResult.CONTINUE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
return Files.deleteIfExists(root); |
|
|
|
} |
|
|
|
} |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -61,43 +97,44 @@ public abstract class FileSystemUtils { |
|
|
|
* @param dest the destination directory |
|
|
|
* @param dest the destination directory |
|
|
|
* @throws IOException in the case of I/O errors |
|
|
|
* @throws IOException in the case of I/O errors |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static void copyRecursively(@Nullable File src, File dest) throws IOException { |
|
|
|
public static void copyRecursively(File src, File dest) throws IOException { |
|
|
|
Assert.isTrue(src != null && (src.isDirectory() || src.isFile()), |
|
|
|
Assert.notNull(src, "Source File must not be null"); |
|
|
|
"Source File must denote a directory or file"); |
|
|
|
|
|
|
|
Assert.notNull(dest, "Destination File must not be null"); |
|
|
|
Assert.notNull(dest, "Destination File must not be null"); |
|
|
|
doCopyRecursively(src, dest); |
|
|
|
copyRecursively(src.toPath(), dest.toPath()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Actually copy the contents of the {@code src} file/directory |
|
|
|
* Recursively copy the contents of the {@code src} file/directory |
|
|
|
* to the {@code dest} file/directory. |
|
|
|
* to the {@code dest} file/directory. |
|
|
|
* @param src the source directory |
|
|
|
* @param src the source directory |
|
|
|
* @param dest the destination directory |
|
|
|
* @param dest the destination directory |
|
|
|
* @throws IOException in the case of I/O errors |
|
|
|
* @throws IOException in the case of I/O errors |
|
|
|
|
|
|
|
* @since 5.0 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private static void doCopyRecursively(File src, File dest) throws IOException { |
|
|
|
public static void copyRecursively(Path src, Path dest) throws IOException { |
|
|
|
if (src.isDirectory()) { |
|
|
|
Assert.notNull(src, "Source Path must not be null"); |
|
|
|
dest.mkdir(); |
|
|
|
Assert.notNull(dest, "Destination Path must not be null"); |
|
|
|
File[] entries = src.listFiles(); |
|
|
|
BasicFileAttributes srcAttr = Files.readAttributes(src, BasicFileAttributes.class); |
|
|
|
if (entries == null) { |
|
|
|
|
|
|
|
throw new IOException("Could not list files in directory: " + src); |
|
|
|
if (srcAttr.isDirectory()) { |
|
|
|
} |
|
|
|
Files.walkFileTree(src, new SimpleFileVisitor<Path>() { |
|
|
|
for (File entry : entries) { |
|
|
|
@Override |
|
|
|
doCopyRecursively(entry, new File(dest, entry.getName())); |
|
|
|
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { |
|
|
|
} |
|
|
|
Files.createDirectories(dest.resolve(src.relativize(dir))); |
|
|
|
|
|
|
|
return FileVisitResult.CONTINUE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { |
|
|
|
|
|
|
|
Files.copy(file, dest.resolve(src.relativize(file))); |
|
|
|
|
|
|
|
return FileVisitResult.CONTINUE; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (src.isFile()) { |
|
|
|
else if (srcAttr.isRegularFile()) { |
|
|
|
try { |
|
|
|
Files.copy(src, dest); |
|
|
|
dest.createNewFile(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (IOException ex) { |
|
|
|
|
|
|
|
throw new IOException("Failed to create file: " + dest, ex); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
FileCopyUtils.copy(src, dest); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
// Special File handle: neither a file not a directory.
|
|
|
|
throw new IllegalArgumentException("Source File must denote a directory or file"); |
|
|
|
// Simply skip it when contained in nested directory...
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|