Delete directories recursively in Java - java

Is there a way to delete entire directories recursively in Java?
In the normal case it is possible to delete an empty directory.
However when it comes to deleting entire directories with contents, it is not that simple anymore.
How do you delete entire directories with contents in Java?

You should check out Apache's commons-io. It has a FileUtils class that will do what you want.
FileUtils.deleteDirectory(new File("directory"));

With Java 7, we can finally do this with reliable symlink detection. (I don't consider Apache's commons-io to have reliable symlink detection at this time, as it doesn't handle links on Windows created with mklink.)
For the sake of history, here's a pre-Java 7 answer, which follows symlinks.
void delete(File f) throws IOException {
if (f.isDirectory()) {
for (File c : f.listFiles())
delete(c);
}
if (!f.delete())
throw new FileNotFoundException("Failed to delete file: " + f);
}

In Java 7+ you can use Files class. Code is very simple:
Path directory = Paths.get("/tmp");
Files.walkFileTree(directory, 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;
}
});

One-liner solution (Java8) to delete all files and directories recursively including starting directory:
try (var dirStream = Files.walk(Paths.get("c:/dir_to_delete/"))) {
dirStream
.map(Path::toFile)
.sorted(Comparator.reverseOrder())
.forEach(File::delete);
}
We use a comparator for reversed order, otherwise File::delete won't be able to delete possibly non-empty directory. So, if you want to keep directories and only delete files just remove the comparator in sorted() or remove sorting completely and add files filter:
try (var dirStream = Files.walk(Paths.get("c:/dir_to_delete/"))) {
dirStream
.filter(Files::isRegularFile)
.map(Path::toFile)
.forEach(File::delete);
}

Java 7 added support for walking directories with symlink handling:
import java.nio.file.*;
public static void removeRecursive(Path path) throws IOException
{
Files.walkFileTree(path, new SimpleFileVisitor<Path>()
{
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException
{
Files.delete(file);
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException
{
// try to delete the file anyway, even if its attributes
// could not be read, since delete-only access is
// theoretically possible
Files.delete(file);
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException
{
if (exc == null)
{
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
else
{
// directory iteration failed; propagate exception
throw exc;
}
}
});
}
I use this as a fallback from platform-specific methods (in this untested code):
public static void removeDirectory(Path directory) throws IOException
{
// does nothing if non-existent
if (Files.exists(directory))
{
try
{
// prefer OS-dependent directory removal tool
if (SystemUtils.IS_OS_WINDOWS)
Processes.execute("%ComSpec%", "/C", "RD /S /Q \"" + directory + '"');
else if (SystemUtils.IS_OS_UNIX)
Processes.execute("/bin/rm", "-rf", directory.toString());
}
catch (ProcessExecutionException | InterruptedException e)
{
// fallback to internal implementation on error
}
if (Files.exists(directory))
removeRecursive(directory);
}
}
(SystemUtils is from Apache Commons Lang. Processes is private but its behavior should be obvious.)

Just saw my solution is more or less the same as erickson's, just packaged as a static method. Drop this somewhere, it's much lighter weight than installing all of Apache Commons for something that (as you can see) is quite simple.
public class FileUtils {
/**
* By default File#delete fails for non-empty directories, it works like "rm".
* We need something a little more brutual - this does the equivalent of "rm -r"
* #param path Root File Path
* #return true iff the file and all sub files/directories have been removed
* #throws FileNotFoundException
*/
public static boolean deleteRecursive(File path) throws FileNotFoundException{
if (!path.exists()) throw new FileNotFoundException(path.getAbsolutePath());
boolean ret = true;
if (path.isDirectory()){
for (File f : path.listFiles()){
ret = ret && deleteRecursive(f);
}
}
return ret && path.delete();
}
}

A solution with a stack and without recursive methods:
File dir = new File("/path/to/dir");
File[] currList;
Stack<File> stack = new Stack<File>();
stack.push(dir);
while (! stack.isEmpty()) {
if (stack.lastElement().isDirectory()) {
currList = stack.lastElement().listFiles();
if (currList.length > 0) {
for (File curr: currList) {
stack.push(curr);
}
} else {
stack.pop().delete();
}
} else {
stack.pop().delete();
}
}

If you have Spring, you can use FileSystemUtils.deleteRecursively:
import org.springframework.util.FileSystemUtils;
boolean success = FileSystemUtils.deleteRecursively(new File("directory"));

Guava had Files.deleteRecursively(File) supported until Guava 9.
From Guava 10:
Deprecated. This method suffers from poor symlink detection and race conditions. This functionality can be supported suitably only by shelling out to an operating system command such as rm -rf or del /s. This method is scheduled to be removed from Guava in Guava release 11.0.
Therefore, there is no such method in Guava 11.

for(Path p : Files.walk(directoryToDelete).
sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
toArray(Path[]::new))
{
Files.delete(p);
}
Or if you want to handle the IOException:
Files.walk(directoryToDelete).
sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
forEach(p -> {
try { Files.delete(p); }
catch(IOException e) { /* ... */ }
});

public void deleteRecursive(File path){
File[] c = path.listFiles();
System.out.println("Cleaning out folder:" + path.toString());
for (File file : c){
if (file.isDirectory()){
System.out.println("Deleting file:" + file.toString());
deleteRecursive(file);
file.delete();
} else {
file.delete();
}
}
path.delete();
}

public static void deleteDirectory(File path)
{
if (path == null)
return;
if (path.exists())
{
for(File f : path.listFiles())
{
if(f.isDirectory())
{
deleteDirectory(f);
f.delete();
}
else
{
f.delete();
}
}
path.delete();
}
}

Two ways to fail with symlinks and the above code... and don't know the solution.
Way #1
Run this to create a test:
echo test > testfile
mkdir dirtodelete
ln -s badlink dirtodelete/badlinktodelete
Here you see your test file and test directory:
$ ls testfile dirtodelete
testfile
dirtodelete:
linktodelete
Then run your commons-io deleteDirectory(). It crashes saying the file is not found. Not sure what the other examples do here. The Linux rm command would simply delete the link, and rm -r on the directory would also.
Exception in thread "main" java.io.FileNotFoundException: File does not exist: /tmp/dirtodelete/linktodelete
Way #2
Run this to create a test:
mkdir testdir
echo test > testdir/testfile
mkdir dirtodelete
ln -s ../testdir dirtodelete/dirlinktodelete
Here you see your test file and test directory:
$ ls dirtodelete testdir
dirtodelete:
dirlinktodelete
testdir:
testfile
Then run your commons-io deleteDirectory() or the example code people posted. It deletes not only the directory, but your testfile which is outside the directory being deleted. (It dereferences the directory implicitly, and deletes the contents). rm -r would delete the link only. You need to use something like this delete the dereferenced files: "find -L dirtodelete -type f -exec rm {} \;".
$ ls dirtodelete testdir
ls: cannot access dirtodelete: No such file or directory
testdir:

You could use:
org.apache.commons.io.FileUtils.deleteQuietly(destFile);
Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
The difference between File.delete() and this method are:
A directory to be deleted does not have to be empty.
No exceptions are thrown when a file or directory cannot be deleted.

An optimal solution that handles exception consistently with the approach that an exception thrown from a method should always describe what that method was trying (and failed) to do:
private void deleteRecursive(File f) throws Exception {
try {
if (f.isDirectory()) {
for (File c : f.listFiles()) {
deleteRecursive(c);
}
}
if (!f.delete()) {
throw new Exception("Delete command returned false for file: " + f);
}
}
catch (Exception e) {
throw new Exception("Failed to delete the folder: " + f, e);
}
}

In legacy projects, I need to create native Java code. I create this code similar to Paulitex code. See that:
public class FileHelper {
public static boolean delete(File fileOrFolder) {
boolean result = true;
if(fileOrFolder.isDirectory()) {
for (File file : fileOrFolder.listFiles()) {
result = result && delete(file);
}
}
result = result && fileOrFolder.delete();
return result;
}
}
And the unit test:
public class FileHelperTest {
#Before
public void setup() throws IOException {
new File("FOLDER_TO_DELETE/SUBFOLDER").mkdirs();
new File("FOLDER_TO_DELETE/SUBFOLDER_TWO").mkdirs();
new File("FOLDER_TO_DELETE/SUBFOLDER_TWO/TEST_FILE.txt").createNewFile();
}
#Test
public void deleteFolderWithFiles() {
File folderToDelete = new File("FOLDER_TO_DELETE");
Assert.assertTrue(FileHelper.delete(folderToDelete));
Assert.assertFalse(new File("FOLDER_TO_DELETE").exists());
}
}

Below code recursively delete all contents in a given folder.
boolean deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
for (File file : allContents) {
deleteDirectory(file);
}
}
return directoryToBeDeleted.delete();
}

Here is a bare bones main method that accepts a command line argument, you may need to append your own error checking or mold it to how you see fit.
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
public class DeleteFiles {
/**
* #param intitial arguments take in a source to read from and a
* destination to read to
*/
public static void main(String[] args)
throws FileNotFoundException,IOException {
File src = new File(args[0]);
if (!src.exists() ) {
System.out.println("FAILURE!");
}else{
// Gathers files in directory
File[] a = src.listFiles();
for (int i = 0; i < a.length; i++) {
//Sends files to recursive deletion method
fileDelete(a[i]);
}
// Deletes original source folder
src.delete();
System.out.println("Success!");
}
}
/**
* #param srcFile Source file to examine
* #throws FileNotFoundException if File not found
* #throws IOException if File not found
*/
private static void fileDelete(File srcFile)
throws FileNotFoundException, IOException {
// Checks if file is a directory
if (srcFile.isDirectory()) {
//Gathers files in directory
File[] b = srcFile.listFiles();
for (int i = 0; i < b.length; i++) {
//Recursively deletes all files and sub-directories
fileDelete(b[i]);
}
// Deletes original sub-directory file
srcFile.delete();
} else {
srcFile.delete();
}
}
}
I hope that helps!

Guava provides a one-liner: MoreFiles.deleteRecursively().
Unlike many of the examples shared, it accounts for symbolic links and will not (by default) delete files outside the provided path.

Maybe a solution for this problem might be to reimplement the delete method of the File class using the code from erickson's answer:
public class MyFile extends File {
... <- copy constructor
public boolean delete() {
if (f.isDirectory()) {
for (File c : f.listFiles()) {
return new MyFile(c).delete();
}
} else {
return f.delete();
}
}
}

Without Commons IO and < Java SE 7
public static void deleteRecursive(File path){
path.listFiles(new FileFilter() {
#Override
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
pathname.listFiles(this);
pathname.delete();
} else {
pathname.delete();
}
return false;
}
});
path.delete();
}

rm -rf was much more performant than FileUtils.deleteDirectory.
After extensive benchmarking, we found that using rm -rf was multiple times faster than using FileUtils.deleteDirectory.
Of course, if you have a small or simple directory, it won't matter but in our case we had multiple gigabytes and deeply nested sub directories where it would take over 10 minutes with FileUtils.deleteDirectory and only 1 minute with rm -rf.
Here's our rough Java implementation to do that:
// Delete directory given and all subdirectories and files (i.e. recursively).
//
static public boolean deleteDirectory( File file ) throws IOException, InterruptedException {
if ( file.exists() ) {
String deleteCommand = "rm -rf " + file.getAbsolutePath();
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec( deleteCommand );
process.waitFor();
return true;
}
return false;
}
Worth trying if you're dealing with large or complex directories.

// Java 8 with lambda & stream, if param is directory
static boolean delRecursive(File dir) {
return Arrays.stream(dir.listFiles()).allMatch((f) -> f.isDirectory() ? delRecursive(f) : f.delete()) && dir.delete();
}
// if param is file or directory
static boolean delRecursive(File fileOrDir) {
return fileOrDir.isDirectory() ? Arrays.stream(fileOrDir.listFiles()).allMatch((f) -> delRecursive(f)) && fileOrDir.delete() : fileOrDir.delete();
}

Guava 21.0 and later
There is the void deleteRecursively(Path path, RecursiveDeleteOption... options) throws IOException static method of the MoreFiles class available since Guava 21.0.
Please, see the Javadoc documentation:
public static void deleteRecursively(Path path,
RecursiveDeleteOption... options)
throws IOException
Deletes the file or directory at the given path recursively. Deletes symbolic links, not their targets (subject to the caveat below).
If an I/O exception occurs attempting to read, open or delete any file under the given directory, this method skips that file and continues. All such exceptions are collected and, after attempting to delete all files, an IOException is thrown containing those exceptions as suppressed exceptions.

While files can easily be deleted using file.delete(), directories are required to be empty to be deleted. Use recursion to do this easily. For example:
public static void clearFolders(String[] args) {
for(String st : args){
File folder = new File(st);
if (folder.isDirectory()) {
File[] files = folder.listFiles();
if(files!=null) {
for(File f: files) {
if (f.isDirectory()){
clearFolders(new String[]{f.getAbsolutePath()});
f.delete();
} else {
f.delete();
}
}
}
}
}
}

i coded this routine that has 3 safety criteria for safer use.
package ch.ethz.idsc.queuey.util;
import java.io.File;
import java.io.IOException;
/** recursive file/directory deletion
*
* safety from erroneous use is enhanced by three criteria
* 1) checking the depth of the directory tree T to be deleted
* against a permitted upper bound "max_depth"
* 2) checking the number of files to be deleted #F
* against a permitted upper bound "max_count"
* 3) if deletion of a file or directory fails, the process aborts */
public final class FileDelete {
/** Example: The command
* FileDelete.of(new File("/user/name/myapp/recordings/log20171024"), 2, 1000);
* deletes given directory with sub directories of depth of at most 2,
* and max number of total files less than 1000. No files are deleted
* if directory tree exceeds 2, or total of files exceed 1000.
*
* abort criteria are described at top of class
*
* #param file
* #param max_depth
* #param max_count
* #return
* #throws Exception if criteria are not met */
public static FileDelete of(File file, int max_depth, int max_count) throws IOException {
return new FileDelete(file, max_depth, max_count);
}
// ---
private final File root;
private final int max_depth;
private int removed = 0;
/** #param root file or a directory. If root is a file, the file will be deleted.
* If root is a directory, the directory tree will be deleted.
* #param max_depth of directory visitor
* #param max_count of files to delete
* #throws IOException */
private FileDelete(final File root, final int max_depth, final int max_count) throws IOException {
this.root = root;
this.max_depth = max_depth;
// ---
final int count = visitRecursively(root, 0, false);
if (count <= max_count) // abort criteria 2)
visitRecursively(root, 0, true);
else
throw new IOException("more files to be deleted than allowed (" + max_count + "<=" + count + ") in " + root);
}
private int visitRecursively(final File file, final int depth, final boolean delete) throws IOException {
if (max_depth < depth) // enforce depth limit, abort criteria 1)
throw new IOException("directory tree exceeds permitted depth");
// ---
int count = 0;
if (file.isDirectory()) // if file is a directory, recur
for (File entry : file.listFiles())
count += visitRecursively(entry, depth + 1, delete);
++count; // count file as visited
if (delete) {
final boolean deleted = file.delete();
if (!deleted) // abort criteria 3)
throw new IOException("cannot delete " + file.getAbsolutePath());
++removed;
}
return count;
}
public int deletedCount() {
return removed;
}
public void printNotification() {
int count = deletedCount();
if (0 < count)
System.out.println("deleted " + count + " file(s) in " + root);
}
}

Related

Listing files in ftp server without the use of recursion [duplicate]

I'm trying to produce file listing of a given directory and it's sub directories in a ftp server.
The server works fine, and I have been successfully able to produce the file listing of the current directory. When I try to list the subdirectories and their files is where it gets complicated.
I was asked not to use a recursion algorithm, so I did some research of my own. I have tried using threads (for every directory found, start a new thread), but I wasn't able to keep my connection stable and open. Any ideas on how to do so correctly with threads, or other alternatives?
EDIT: below is my code, when using the recursive statement (last line of code), it works
class TEST {
public static synchronized void main(String[] args) {
String server = args[0]; //server,path will be given as an arguments
String pass = "SOMEPASS";
String user = "SOMEUSER";
int port = 21;
FTPClient ftpClient = new FTPClient();
try {
ftpClient.connect(server, port);
showServerReply(ftpClient);
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
System.out.println("Connect failed");
return;
}
boolean success = ftpClient.login(user, pass);
showServerReply(ftpClient);
if (!success) {
System.out.println("Could not login to the server");
return;
}
/*START THE FILE LISTING HERE*/
} catch (IOException ex) {
System.out.println("Oops! Something wrong happened");
ex.printStackTrace();
} finally {
// logs out and disconnects from server
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
private static void showServerReply(FTPClient ftpClient) {
String[] replies = ftpClient.getReplyStrings();
if (replies != null && replies.length > 0) {
for (String aReply : replies) {
System.out.println("SERVER: " + aReply);
}
}
}
private static void scanDir(FTPClient client, String path) throws IOException {
FTPFile[] files = client.listFiles(path); // Search all the files in the current directory
for (int j = 0; j < files.length; j++) {
System.out.println(files[j].getName()); // Print the name of each files
}
FTPFile[] directories = client.listDirectories(path); // Search all the directories in the current directory
for (int i = 0; i < directories.length; i++) {
String dirPath = directories[i].getName();
System.out.println(dirPath); // Print the path of a sub-directory
scanDir(client,dirPath); // Call recursively the method to display the files in the sub-directory DONT WANT TO DO THAT...
}
}
}
Okay, here is an example of how to handle it non-recursively, but with lists.
Mind, that this example is based on /accessing the local filesystem, but can easily be rewritten/extended for any kind of hierarchial/recursive structure.
package stackoverflow.nonrecursivefilesearch;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.stream.Stream;
public class NonRecursiveFileSearch {
public static void main(final String[] args) throws IOException {
final File searchDir = new File("D:\\test\\maven-test"); // set one
System.out.println("\nOld Java");
printDirs(listFiles_old(searchDir, true, true), "OLD: Depth first, include dirs");
printDirs(listFiles_old(searchDir, true, false), "OLD: Breadth first, include dirs");
printDirs(listFiles_old(searchDir, false, true), "OLD: Depth first, exclude dirs");
printDirs(listFiles_old(searchDir, false, false), "OLD: Breadth first, exclude dirs");
System.out.println("\nNew java.io with streams");
printDirs(listFiles_newIO(searchDir, true), "Java NIO, include dirs");
printDirs(listFiles_newIO(searchDir, false), "Java NIO, exclude dirs");
}
/**
* this is the way to 'manually' find files in hierarchial/recursive structures
*
* reminder: "Depth First" is not a real depth-first implementation
* real depth-first would iterate subdirs immediately.
* this implementation iterates breadth first, but descends into supdirs before it handles same-level directories
* advantage of this implementation is its speed, no need for additional lists etc.
*
* in case you want to exclude recursion traps made possible by symbolic or hard links, you could introduce a hashset/treeset with
* visited files (use filename strings retrieved with canonicalpath).
* in the loop, check if the current canonical filename string is contained in the hash/treeset
*/
static public ArrayList<File> listFiles_old(final File pDir, final boolean pIncludeDirectories, final boolean pDepthFirst) {
final ArrayList<File> found = new ArrayList<>();
final ArrayList<File> todo = new ArrayList<>();
todo.add(pDir);
while (todo.size() > 0) {
final int removeIndex = pDepthFirst ? todo.size() - 1 : 0;
final File currentDir = todo.remove(removeIndex);
if (currentDir == null || !currentDir.isDirectory()) continue;
final File[] files = currentDir.listFiles();
for (final File file : files) {
if (file.isDirectory()) {
if (pIncludeDirectories) found.add(file);
// additional directory filters go here
todo.add(file);
} else {
// additional file filters go here
found.add(file);
}
}
}
return found;
}
static private void printDirs(final ArrayList<File> pFiles, final String pTitle) {
System.out.println("====================== " + pTitle + " ======================");
for (int i = 0; i < pFiles.size(); i++) {
final File file = pFiles.get(i);
System.out.println(i + "\t" + file.getAbsolutePath());
}
System.out.println("============================================================");
}
/**
* this is the java.nio approach. this is NOT be a good solution for cases where you have to retrieve/handle files in your own code.
* this is only useful, if the any NIO class provides support. in this case, NIO class java.nio.file.Files helps handling local files.
* if NIO or your target system does not offer such helper methods, this way is harder to implement, as you have to set up the helper method yourself.
*/
static public Stream<Path> listFiles_newIO(final File pDir, final boolean pIncludeDirectories) throws IOException {
final Stream<Path> stream = Files.find(pDir.toPath(), 100,
(path, basicFileAttributes) -> {
final File file = path.toFile(); // conversion to File for easier access (f.e. isDirectory()), could also use NIO methods
return (pIncludeDirectories || !file.isDirectory() /* additional filters go here */ );
});
return stream;
}
static private void printDirs(final Stream<Path> pStream, final String pTitle) {
System.out.println("====================== " + pTitle + " ======================");
pStream.forEach(System.out::println);
System.out.println("============================================================");
}
}
AND, one must add, java.nio.file.Files.find() might be implemented recursively. But as it's just one call, this maybe could count as 'non-recursive' too.
ALSO, as the OP stated in comments, one might use Stack or other FIFO/LIFO collections. LIFO for a mixed depth-first, FIFO for breadth-first approach.

How to delete all files in directory but keep the file that a symbolic link is pointing to?

This has been identified as a possible duplicate of another question on here, it is not. I know how to delete the symbolic link and the files. I am trying to keep the file associated with the symbolic link but delete everything else.
My Problem: Delete all files in directory older than 7 days except files associated with symbolic link.
Issue: Symbolic link successfully deleted but remaining older files are not removed.
Details: I am trying to write a simple Java program to delete all of the files and subfolders in a directory older than 7 days which is working but there is one issue. If there is a symbolic link then I need to delete just the link and keep the file that it links to. Other than that case, everything else gets deleted. I know that I am very close... if you have any suggestions let me know please. At the moment I am able to have it delete the symbolic link but the other old files are not being deleted when they should. This could be a simple logic error or maybe I am approaching the problem the wrong way. Thanks in advance!
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class delcache {
public static void main(String[] args) throws IOException
{
String path1="/home/test/cachetest";
recursiveDelete(7, path1);
}
public static void recursiveDelete(int days, String dirPath) throws IOException {
long cutOff = System.currentTimeMillis() - (days * 24 * 60 * 60 * 1000);
Files.list(Paths.get(dirPath))
.forEach(path -> {
if (Files.isDirectory(path)) {
try {
recursiveDelete(days, path.toString());
} catch (IOException e) {
// log
}
} else {
try {
if (Files.getLastModifiedTime(path).to(TimeUnit.MILLISECONDS) < cutOff) {
String pathsave = null;
if(Files.isSymbolicLink(path)) {
pathsave = path.toRealPath().toString();
System.out.println("pathsave: " + pathsave);
System.out.println("delete symlink: " + path);
Files.delete(path);
}
if(!(Files.isSymbolicLink(path))) {
System.out.println("pathsave: " + pathsave);
if(!(path.toString().equals(pathsave))) {
System.out.println("not equal so delete file: " + path);
Files.delete(path);
}
}
//Files.delete(path);
}
} catch (IOException ex) {
// log
}
}
});
}
}
Your logic is flawed, String pathsave persists only for the current loop of the specific path. when the loop gets to actual file the pathsave is always null.
if you want to save the symbolic links path you need to have a list of paths which is outside the function. even then it won't work because you have no guarantee for any order, you may first reach to the symbolic link or first reach the file.
So as I understand it you must first iterate the folder looking for all the symbolic links, save them into global List which is accessible to the method, and then run and delete the files.
side note: notice that if you delete the symboic links, the next time you would run this function, the same files which previously had symlinks will now be deleted
public static void main(String[] args) throws IOException
{
String path1="/home/test/cachetest";
List<Path> symlinks = getAllSymLinks(path1);
recursiveDelete(7, path1, symlinks);
}
public static List<Path> getAllSymLinks(String dirPath) throws IOException {
List<Path> paths = new ArrayList<>();
Files.list(Paths.get(dirPath))
.forEach(path -> {
if (Files.isDirectory(path)) {
try {
paths.addAll(recursiveDelete(days, path.toString()));
} catch (IOException e) {
// log
}
} else {
try {
if(Files.isSymbolicLink(path)) {
paths.add(path.toRealPath());
}
} catch (IOException ex) {
// log
}
}
});
return paths;
}
public static void recursiveDelete(int days, String dirPath, List<path> symlinks) throws IOException {
long cutOff = System.currentTimeMillis() - (days * 24 * 60 * 60 * 1000);
Files.list(Paths.get(dirPath))
.forEach(path -> {
if (Files.isDirectory(path)) {
try {
recursiveDelete(days, path.toString(), symlinks);
} catch (IOException e) {
// log
}
}
else {
try {
if (Files.getLastModifiedTime(path).to(TimeUnit.MILLISECONDS) < cutOff &&
!Files.isSymbolicLink(path) &&
!symlinks.contains(path))
{
Files.delete(path);
}
} catch (IOException ex) {
// log
}
}
});
You could use this method:
Use Process process = Runtime.getRuntime.exec("ls -l") to list all contents of the directory.
Read the InputStream of process line by line.
If the first character is 'd', the file in the line is a directory. Recurse.
If the first character is '-', delete.
If the first character is 'l', it is a symbolic link. Skip the file.
You can get the filename from the same line. For example, if the line is in a variable 'str', the the filename is str.split("\\s+")[8]
You can additionally use the filenames for any other checks you require.
EDIT
Untested method using grep:
String getLinkedFile(file) {
// Input: A Symbolic link file
// Output: Name of file pointed to by the symlink
Process p = Runtime.getRuntime.exec("ls -l | grep -e \"->\"");
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = reader.readLine()) != null) {
String []tokens = line.split("\\s+");
String linkFile = tokens[8];
if(linkFile.equals(file.getName()))
return tokens[10];
}
return null;
}
You may need to modify this function based on file paths.

Recursing through a directory in java, I want to skip a step on the top level

I am trying to output a list of files within a directory recursively (not including the name of the name of the directory that I am starting with (just the contents of it and all files recursing down the tree after that)
here is what I have at the minute. It Might have errors here and there, but the idea is that it will print all the names of every file in the tree recursively. My problem is that I don't want it to print the name of the directory in which they live.
I think my problem is that I am using System.out.println at the start of the recursive method, which means it gets used every time. Which is desirable behavior for every directory BELOW the first one. Its an annoying little problem that I could use some help on. Thanks in advance.
public static void listFiles(String path)
{
File basedir = new File(path);
System.out.println(path.getName());
try
{
File[] files = basedir.listFiles();
for (File file : files)
{
// If Dealing with a directory, call recursively to the function
if (file.isDirectory())
{
listFiles(file.getPath());
}
else
{
System.out.println(file.getName());
}
}
}
catch(IOException ex)
{
System.out.println(ex);
}
}
public static void listFiles(String path, boolean firstCall)
{
File basedir = new File(path);
if(!firstCall)
{
System.out.println(path.getName());
}
try
{
File[] files = basedir.listFiles();
for (File file : files)
{
// If Dealing with a directory, call recursively to the function
if (file.isDirectory())
{
listFiles(file.getPath(), false); //false here because it is not the first call
}
else
{
System.out.println(file.getName());
}
}
}
catch(IOException ex)
{
System.out.println(ex);
}
}
Add a boolean parameter that specifies if it is the first call. When you call the method pass true to the parameter. Also path.getName() is not valid String doesn't have a function getName() maybe you meant basedir.getName()...also remove try catch block IOException can't occur there.

How to know size of folder using java program? [duplicate]

How can I retrieve size of folder or file in Java?
java.io.File file = new java.io.File("myfile.txt");
file.length();
This returns the length of the file in bytes or 0 if the file does not exist. There is no built-in way to get the size of a folder, you are going to have to walk the directory tree recursively (using the listFiles() method of a file object that represents a directory) and accumulate the directory size for yourself:
public static long folderSize(File directory) {
long length = 0;
for (File file : directory.listFiles()) {
if (file.isFile())
length += file.length();
else
length += folderSize(file);
}
return length;
}
WARNING: This method is not sufficiently robust for production use. directory.listFiles() may return null and cause a NullPointerException. Also, it doesn't consider symlinks and possibly has other failure modes. Use this method.
Using java-7 nio api, calculating the folder size can be done a lot quicker.
Here is a ready to run example that is robust and won't throw an exception. It will log directories it can't enter or had trouble traversing. Symlinks are ignored, and concurrent modification of the directory won't cause more trouble than necessary.
/**
* Attempts to calculate the size of a file or directory.
*
* <p>
* Since the operation is non-atomic, the returned value may be inaccurate.
* However, this method is quick and does its best.
*/
public static long size(Path path) {
final AtomicLong size = new AtomicLong(0);
try {
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
size.addAndGet(attrs.size());
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
System.out.println("skipped: " + file + " (" + exc + ")");
// Skip folders that can't be traversed
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
if (exc != null)
System.out.println("had trouble traversing: " + dir + " (" + exc + ")");
// Ignore errors traversing a folder
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new AssertionError("walkFileTree will not throw IOException if the FileVisitor does not");
}
return size.get();
}
You need FileUtils#sizeOfDirectory(File) from commons-io.
Note that you will need to manually check whether the file is a directory as the method throws an exception if a non-directory is passed to it.
WARNING: This method (as of commons-io 2.4) has a bug and may throw IllegalArgumentException if the directory is concurrently modified.
In Java 8:
long size = Files.walk(path).mapToLong( p -> p.toFile().length() ).sum();
It would be nicer to use Files::size in the map step but it throws a checked exception.
UPDATE:
You should also be aware that this can throw an exception if some of the files/folders are not accessible. See this question and another solution using Guava.
public static long getFolderSize(File dir) {
long size = 0;
for (File file : dir.listFiles()) {
if (file.isFile()) {
System.out.println(file.getName() + " " + file.length());
size += file.length();
}
else
size += getFolderSize(file);
}
return size;
}
For Java 8 this is one right way to do it:
Files.walk(new File("D:/temp").toPath())
.map(f -> f.toFile())
.filter(f -> f.isFile())
.mapToLong(f -> f.length()).sum()
It is important to filter out all directories, because the length method isn't guaranteed to be 0 for directories.
At least this code delivers the same size information like Windows Explorer itself does.
Here's the best way to get a general File's size (works for directory and non-directory):
public static long getSize(File file) {
long size;
if (file.isDirectory()) {
size = 0;
for (File child : file.listFiles()) {
size += getSize(child);
}
} else {
size = file.length();
}
return size;
}
Edit: Note that this is probably going to be a time-consuming operation. Don't run it on the UI thread.
Also, here (taken from https://stackoverflow.com/a/5599842/1696171) is a nice way to get a user-readable String from the long returned:
public static String getReadableSize(long size) {
if(size <= 0) return "0";
final String[] units = new String[] { "B", "KB", "MB", "GB", "TB" };
int digitGroups = (int) (Math.log10(size)/Math.log10(1024));
return new DecimalFormat("#,##0.#").format(size/Math.pow(1024, digitGroups))
+ " " + units[digitGroups];
}
File.length() (Javadoc).
Note that this doesn't work for directories, or is not guaranteed to work.
For a directory, what do you want? If it's the total size of all files underneath it, you can recursively walk children using File.list() and File.isDirectory() and sum their sizes.
The File object has a length method:
f = new File("your/file/name");
f.length();
If you want to use Java 8 NIO API, the following program will print the size, in bytes, of the directory it is located in.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathSize {
public static void main(String[] args) {
Path path = Paths.get(".");
long size = calculateSize(path);
System.out.println(size);
}
/**
* Returns the size, in bytes, of the specified <tt>path</tt>. If the given
* path is a regular file, trivially its size is returned. Else the path is
* a directory and its contents are recursively explored, returning the
* total sum of all files within the directory.
* <p>
* If an I/O exception occurs, it is suppressed within this method and
* <tt>0</tt> is returned as the size of the specified <tt>path</tt>.
*
* #param path path whose size is to be returned
* #return size of the specified path
*/
public static long calculateSize(Path path) {
try {
if (Files.isRegularFile(path)) {
return Files.size(path);
}
return Files.list(path).mapToLong(PathSize::calculateSize).sum();
} catch (IOException e) {
return 0L;
}
}
}
The calculateSize method is universal for Path objects, so it also works for files.
Note that if a file or directory is inaccessible, in this case the returned size of the path object will be 0.
Works for Android and Java
Works for both folders and files
Checks for null pointer everywhere where needed
Ignores symbolic link aka shortcuts
Production ready!
Source code:
public long fileSize(File root) {
if(root == null){
return 0;
}
if(root.isFile()){
return root.length();
}
try {
if(isSymlink(root)){
return 0;
}
} catch (IOException e) {
e.printStackTrace();
return 0;
}
long length = 0;
File[] files = root.listFiles();
if(files == null){
return 0;
}
for (File file : files) {
length += fileSize(file);
}
return length;
}
private static boolean isSymlink(File file) throws IOException {
File canon;
if (file.getParent() == null) {
canon = file;
} else {
File canonDir = file.getParentFile().getCanonicalFile();
canon = new File(canonDir, file.getName());
}
return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
}
I've tested du -c <folderpath> and is 2x faster than nio.Files or recursion
private static long getFolderSize(File folder){
if (folder != null && folder.exists() && folder.canRead()){
try {
Process p = new ProcessBuilder("du","-c",folder.getAbsolutePath()).start();
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
String total = "";
for (String line; null != (line = r.readLine());)
total = line;
r.close();
p.waitFor();
if (total.length() > 0 && total.endsWith("total"))
return Long.parseLong(total.split("\\s+")[0]) * 1024;
} catch (Exception ex) {
ex.printStackTrace();
}
}
return -1;
}
for windows, using java.io this reccursive function is useful.
public static long folderSize(File directory) {
long length = 0;
if (directory.isFile())
length += directory.length();
else{
for (File file : directory.listFiles()) {
if (file.isFile())
length += file.length();
else
length += folderSize(file);
}
}
return length;
}
This is tested and working properly on my end.
private static long getFolderSize(Path folder) {
try {
return Files.walk(folder)
.filter(p -> p.toFile().isFile())
.mapToLong(p -> p.toFile().length())
.sum();
} catch (IOException e) {
e.printStackTrace();
return 0L;
}
public long folderSize (String directory)
{
File curDir = new File(directory);
long length = 0;
for(File f : curDir.listFiles())
{
if(f.isDirectory())
{
for ( File child : f.listFiles())
{
length = length + child.length();
}
System.out.println("Directory: " + f.getName() + " " + length + "kb");
}
else
{
length = f.length();
System.out.println("File: " + f.getName() + " " + length + "kb");
}
length = 0;
}
return length;
}
After lot of researching and looking into different solutions proposed here at StackOverflow. I finally decided to write my own solution. My purpose is to have no-throw mechanism because I don't want to crash if the API is unable to fetch the folder size. This method is not suitable for mult-threaded scenario.
First of all I want to check for valid directories while traversing down the file system tree.
private static boolean isValidDir(File dir){
if (dir != null && dir.exists() && dir.isDirectory()){
return true;
}else{
return false;
}
}
Second I do not want my recursive call to go into symlinks (softlinks) and include the size in total aggregate.
public static boolean isSymlink(File file) throws IOException {
File canon;
if (file.getParent() == null) {
canon = file;
} else {
canon = new File(file.getParentFile().getCanonicalFile(),
file.getName());
}
return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
}
Finally my recursion based implementation to fetch the size of the specified directory. Notice the null check for dir.listFiles(). According to javadoc there is a possibility that this method can return null.
public static long getDirSize(File dir){
if (!isValidDir(dir))
return 0L;
File[] files = dir.listFiles();
//Guard for null pointer exception on files
if (files == null){
return 0L;
}else{
long size = 0L;
for(File file : files){
if (file.isFile()){
size += file.length();
}else{
try{
if (!isSymlink(file)) size += getDirSize(file);
}catch (IOException ioe){
//digest exception
}
}
}
return size;
}
}
Some cream on the cake, the API to get the size of the list Files (might be all of files and folder under root).
public static long getDirSize(List<File> files){
long size = 0L;
for(File file : files){
if (file.isDirectory()){
size += getDirSize(file);
} else {
size += file.length();
}
}
return size;
}
in linux if you want to sort directories then du -hs * | sort -h
You can use Apache Commons IO to find the folder size easily.
If you are on maven, please add the following dependency in your pom.xml file.
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
If not a fan of Maven, download the following jar and add it to the class path.
https://repo1.maven.org/maven2/commons-io/commons-io/2.6/commons-io-2.6.jar
public long getFolderSize() {
File folder = new File("src/test/resources");
long size = FileUtils.sizeOfDirectory(folder);
return size; // in bytes
}
To get file size via Commons IO,
File file = new File("ADD YOUR PATH TO FILE");
long fileSize = FileUtils.sizeOf(file);
System.out.println(fileSize); // bytes
It is also achievable via Google Guava
For Maven, add the following:
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>
If not using Maven, add the following to class path
https://repo1.maven.org/maven2/com/google/guava/guava/28.1-jre/guava-28.1-jre.jar
public long getFolderSizeViaGuava() {
File folder = new File("src/test/resources");
Iterable<File> files = Files.fileTreeTraverser()
.breadthFirstTraversal(folder);
long size = StreamSupport.stream(files.spliterator(), false)
.filter(f -> f.isFile())
.mapToLong(File::length).sum();
return size;
}
To get file size,
File file = new File("PATH TO YOUR FILE");
long s = file.length();
System.out.println(s);
fun getSize(context: Context, uri: Uri?): Float? {
var fileSize: String? = null
val cursor: Cursor? = context.contentResolver
.query(uri!!, null, null, null, null, null)
try {
if (cursor != null && cursor.moveToFirst()) {
// get file size
val sizeIndex: Int = cursor.getColumnIndex(OpenableColumns.SIZE)
if (!cursor.isNull(sizeIndex)) {
fileSize = cursor.getString(sizeIndex)
}
}
} finally {
cursor?.close()
}
return fileSize!!.toFloat() / (1024 * 1024)
}

Recursively Deleting a Directory

I have this section of code:
public static void delete(File f) throws IOException
{
if (f.isDirectory())
{
for (File c : f.listFiles())
{
delete(c);
}
}
else if (!f.delete())
{
throw new FileNotFoundException("Failed to delete file: " + f);
}
}
public static void traverseDelete(File directory) throws FileNotFoundException, InterruptedException
{
//Get all files in directory
File[] files = directory.listFiles();
for (File file : files)
{
if (file.getName().equalsIgnoreCase("word"))
{
boolean containsMedia = false;
File[] filesInWordFolder = file.listFiles();
for ( File file2 : filesInWordFolder )
{
if ( file2.getName().contains("media"))
{
containsMedia = true;
break;
}
}
if (containsMedia == false)
{
try
{
delete(file.getParentFile());
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
else if (file.isDirectory())
{
traverseDelete(file);
}
}
}
Sorry for the lack of commenting, but it's pretty self-explanatory, I think. Essentially what the code is supposed to do is traverses a set of files in a given directory, if it encounters a directory named "word", then it should list out the contents of word, and then if a directory called "media" does NOT exist, recursively delete everything within the parent directory of "word" down.
My main concern comes from this conditional:
if(!filesInWordFolder.toString().contains("media"))
Is that the correct way to say if the files in that array does not contain an instance of "image", go ahead and delete?
That won't work.
File[] filesInWordFolder = file.listFiles();
if(!filesInWordFolder.toString().contains("media"))
will give you a string representation of a File array -- which will typically have a reference.
You have to iterate through the files to find out if there's any in there that contain the word media.
boolean containsMedia = false;
for ( File file : filesInWordFolder ) {
if ( file.getName().contains("media") ){
containsMedia = true;
break;
}
// now check your boolean
if ( !containsMedia ) {
Well using toString() will give you a String representation of the file (in this case the files). The String representation should contain the file name. If your set purpose is to check for any instance of a file containing the word "media" in the directory, you are fine.
In the example you are printing the String representation of the File array. Instead you should iterate through the File array and check the String representation of each individual File as so:
for (int i = 0; i < file_array.length; i++) {
if ((File)file_array[i]).toString().equals("your_search_term")) {
// The file contains your search term
} else {
// Doesn't contain the search term.
}
}

Categories

Resources