Usuń katalogi rekurencyjnie w Javie


382

Czy istnieje sposób, aby rekurencyjnie usuwać całe katalogi w Javie?

W normalnym przypadku możliwe jest usunięcie pustego katalogu. Jednak jeśli chodzi o usuwanie całych katalogów z zawartością, nie jest to już takie proste.

Jak usunąć całe katalogi z zawartością w Javie?


4
File.delete () powinien po prostu zwrócić false po wywołaniu go z niepustym katalogiem.
Ben S

Jeśli używasz Java 8, zobacz odpowiedź @ RoK.
Robin,

Odpowiedzi:


462

Powinieneś sprawdzić commons-io Apache . Ma klasę FileUtils , która zrobi to, co chcesz.

FileUtils.deleteDirectory(new File("directory"));

3
Ta funkcja prawdopodobnie otacza kod, który podał Erickson w swojej odpowiedzi.
paweloque

14
To jest trochę dokładniejsze. Obsługuje takie rzeczy jak dowiązania symboliczne poprawnie w systemie Linux / Unix. svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/…
Steve K



Po co dodawać inną zależność, gdy Java ma gotowe narzędzie? Zobacz odpowiedź RoK na tej stronie lub stackoverflow.com/questions/35988192/...
foo

190

W Javie 7 możemy wreszcie to zrobić dzięki niezawodnemu wykrywaniu dowiązań symbolicznych. (Nie uważam, że commons-io firmy Apache ma teraz niezawodne wykrywanie dowiązań symbolicznych, ponieważ nie obsługuje linków w systemie Windows utworzonych za pomocą mklink.)

Ze względu na historię, oto odpowiedź wcześniejsza niż Java 7, która następuje po dowiązaniach symbolicznych.

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);
}

11
File.delete () nie ma tej funkcji.
Ben S

14
@Erickson: Czy FileNotFoundException nie jest złym wyjątkiem w przypadku niepowodzenia usuwania? Jeśli pliku naprawdę już nie ma, to musi już zostać usunięty, co oznacza, że ​​semantycznie usunięcie nie zakończyło się niepowodzeniem - nie miało nic do zrobienia. A jeśli zawiodło z innego powodu, nie dlatego, że nie znaleziono pliku.
Lawrence Dol

46
Być bardzo ostrożnym . Spowoduje to usunięcie dowiązań symbolicznych. Jeśli korzystasz np. Z systemu Linux i masz folder fooz łączem foo/linktakim, że link->/wywołanie delete(new File(foo)) skasuje tyle systemu plików, ile użytkownik może !!
Miquel

4
@ Miquel To nie ma sensu - dlaczego mielibyśmy być ostrożni? Z pewnością celem dostarczonego kodu jest usunięcie całego katalogu, co wydaje się robić. Nie rozumiem, jakie jest niebezpieczeństwo.
Joehot200

12
@ Joehot200 masz rację, wywołanie delete na symlink katalogu nie usunie katalogu, tylko sam symlink. Usunięcie katalogu wymagałoby jawnego podążenia za dowiązaniem symbolicznym przy użyciu ReadSymbolicLink . Mój błąd! Dobrze zauważony
Miquel,

148

W Javie 7+ możesz używać Filesklasy. Kod jest bardzo prosty:

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;
   }
});

2
To rozwiązanie wydaje się bardzo eleganckie i nie zawiera żadnej logiki przechodzenia przez katalogi!
Zero3

1
„Aby znaleźć perłę zanurz się głęboko w oceanie.”. To zdecydowanie najładniejsze rozwiązanie, jakie znalazłem. Musiałem zanurkować głęboko, aby go znaleźć. Znakomity!
Basil Musa

20
„Kod” NIE jest „bardzo prosty”, aby po prostu usunąć katalog :-) Ale hej, to najlepsze rozwiązanie w czystej Javie.
Mat.

1
Należy pamiętać, że użyte tutaj przeciążenie walkFileTree „ nie podąża za dowiązaniami symbolicznymi ”. (Javadoc: docs.oracle.com/javase/7/docs/api/java/nio/file/… )
Stephan

1
Prawdopodobnie powinieneś wywołać super.postVisitDirectory(dir, exc);swoją postVisitDirectorymetodę, aby wysadzić w powietrze, jeśli spacer nie może wyświetlić katalogu.
Tom Anderson

68

Jednoliniowe rozwiązanie (Java8) do rekurencyjnego usuwania wszystkich plików i katalogów, w tym katalogu startowego:

Files.walk(Paths.get("c:/dir_to_delete/"))
                .map(Path::toFile)
                .sorted((o1, o2) -> -o1.compareTo(o2))
                .forEach(File::delete);

Używamy komparatora w odwrotnej kolejności, w przeciwnym razie File :: delete nie będzie w stanie usunąć potencjalnie niepustego katalogu. Tak więc, jeśli chcesz zachować katalogi i usuwać tylko pliki, po prostu usuń komparator w sorted () lub całkowicie usuń sortowanie i dodaj filtr plików:

Files.walk(Paths.get("c:/dir_to_delete/"))
                .filter(Files::isRegularFile)
                .map(Path::toFile)
                .forEach(File::delete);

1
Musisz zmienić sortowanie w pierwszym na .sorted (Comparator :: reverseOrder), aby usunąć wszystkie katalogi. W przeciwnym razie katalog nadrzędny zostanie uporządkowany przed dzieckiem, a zatem nie zostanie usunięty, ponieważ nie jest pusty. Świetna odpowiedź dla osób korzystających z Java 8!
Robin,

1
Prawidłowy sposób jest .sorted(Comparator.reverseOrder())sugestia Comparator::reverseOrderma nie działać. Zobacz: stackoverflow.com/questions/43036611/…
user1156544

4
Robin, zwróć uwagę na znak minus „-o1.compareTo (o2)”. Jest taki sam jak .sorted (Comparator.reverseOrder)
RoK

Czy Files.walk jest sekwencyjny? Czy może ta odpowiedź wymaga forEachOraged zamiast forEach, aby uniknąć próby usunięcia niepustych katalogów?
Silwing

Wystarczy użyć .sorted((f1, f2) -> f2.compareTo(f1)):, porównując f2z f1zamiast f1z f2.
Beto Neto

67

Java 7 dodała obsługę katalogów kroczących z obsługą dowiązań symbolicznych:

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;
            }
        }
    });
}

Używam tego jako rezerwy metod specyficznych dla platformy (w tym nieprzetestowanym kodzie):

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 pochodzi z Apache Commons Lang . Procesy są prywatne, ale ich zachowanie powinno być oczywiste.)


Znalazłem jeden problem z Files.walkFileTree - jest on niewystarczający do wdrożenia wersji usuwania rekurencyjnego, w której pliki są usuwane do momentu wyczerpania opcji. Jest odpowiedni dla wersji
odpornej na awarie

Nie rozumiem, dlaczego to prawda. Możesz obsłużyć błędy w dowolny sposób - nie musisz szybko zawieść. Jedynym problemem, jaki mogłem przewidzieć, jest to, że może on nie obsługiwać nowych plików tworzonych podczas przejścia do bieżącego katalogu, ale jest to wyjątkowa sytuacja, która lepiej pasuje do niestandardowego rozwiązania.
Trevor Robinson

1
Jeśli pominiesz błąd w visitFile i wywołasz walkFileTree na pojedynczym pliku, który się nie powiedzie, nie pojawi się błąd (więc visitFile musi propagować występujący błąd). Jeśli usuwasz katalog i nie możesz usunąć jednego pliku, jedyne wywołanie zwrotne o nazwie jest postVisitDirectory. tzn. nie odwiedzi innych plików w katalogu, jeśli wystąpi błąd podczas odwiedzania jednego pliku. To jest to co mam na mysli. Jestem pewien, że jest jakiś sposób na obejście tego, ale zanim dotarliśmy do tego momentu, napisaliśmy już więcej kodu niż tradycyjna procedura usuwania rekurencyjnego, więc postanowiliśmy nie używać go.
Trejkaz

Dzięki za twój pierwszy kod, był on dla mnie użyteczny, ale musiałem go zmienić, ponieważ nie ukończył on prostego deltree: musiałem zignorować wyjątek w „postVisitDirectory” i zwrócić KONTYNUUJ niezależnie od tego, że następujące proste drzewo nie mogło w pełni zostać usunięte: katalog, w którym znajdował się inny katalog, w którym znajdował się jeden plik. Wszystko to jest tak proste / normalne, jak to możliwe, w systemie Windows.
Prezydent Dreamspace,

Wszystko zaczęło się od java.nio.file.DirectoryNotEmptyException mam. Dowiedziałem się o przypadku użycia visitFileFailed. Jeśli struktura katalogów zawiera łącze typu połączenia w systemie Windows. Może to powodować 2 problemy: *) Files.walkFileTree podąża za linkiem do skrzyżowania i usuwa wszystko tam. *) Jeśli katalog docelowy skrzyżowania jest już usunięty, wówczas parsowanie łącza przez Files.walkFileTree kończy się niepowodzeniem z wyjątkiem NoSuchFileException, który jest przechwytywany w visitFileFailed.
Andres Luuk

34

Właśnie zobaczyłem, że moje rozwiązanie jest mniej więcej takie samo jak rozwiązanie Ericksona, po prostu spakowane jako metoda statyczna. Upuść to gdzieś, jest znacznie lżejsze niż instalacja całego Apache Commons dla czegoś, co (jak widać) jest dość proste.

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();
    }
}

20

Rozwiązanie ze stosem i bez metod rekurencyjnych:

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();
    }
}

2
+1 za użycie stosu. Będzie to działać z katalogami, które zawierają głębokie poziomy zagnieżdżonych podkatalogów, podczas gdy inne podejścia oparte na stosie zakończą się niepowodzeniem.
Nathan Osman,

4
Widząc, że zwykle nie masz problemów z zagnieżdżeniem kilkuset wywołań metod, myślę, że prawdopodobnie wcześniej napotkasz ograniczenia systemu plików.
Bombe,

2
Uważaj na list*metody klasy java.io.File. Z Javadocs: „Zwraca null, jeśli ta abstrakcyjna nazwa ścieżki nie oznacza katalogu, lub jeśli wystąpi błąd we / wy”. Więc: if (currList.length > 0) {staje sięif (null != currList && currList.length > 0) {
kevinarpe

1
Używam ArrayDeque zamiast stosu, który jest nieco szybszy. (niezsynchronizowane)
Wytze


15

Guawa był Files.deleteRecursively(File)wspierany aż Guava 9 .

Od Guawy 10 :

Przestarzałe. Ta metoda ma słabe wykrywanie dowiązań symbolicznych i warunki wyścigu. Funkcjonalność tę można odpowiednio obsłużyć tylko poprzez wykonanie polecenia systemu operacyjnego, takiego jak rm -rflub del /s. Ta metoda jest planowana do usunięcia z Guava w wersji Guava 11.0.

Dlatego w Guava 11 nie ma takiej metody .


6
Szkoda Wydawanie pocisków wydaje się trochę prymitywne i nieprzenośne. Jeśli wspólna wersja Apache działa poprawnie, prawdopodobnie nie jest to niemożliwe do wdrożenia.
Andrew McKinlay,

6
@andrew Realizacja Apache Commons powinny mieć podobne problemy do tych, które powodują Guava usunąć ich realizacji, patrz code.google.com/p/guava-libraries/issues/detail?id=365
OriP

Wersja wspólna Apache wykrywa dowiązania symboliczne i po prostu nie przechodzą pod potomków pliku.
Ajax,

5
Guava 21.0 dodał to jako MoreFiles.deleteRecursively () .
Robert Fleming,

12
for(Path p : Files.walk(directoryToDelete).
        sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
        toArray(Path[]::new))
{
    Files.delete(p);
}

Lub jeśli chcesz obsłużyć IOException:

Files.walk(directoryToDelete).
    sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
    forEach(p -> {
        try { Files.delete(p); }
        catch(IOException e) { /* ... */ }
      });

2
To pomogło mi wymyślić wersję Scala:Files.walk(path).iterator().toSeq.reverse.foreach(Files.delete)
James Ward

Czy sortowanie jest naprawdę konieczne? Ta walkmetoda już gwarantuje przejście przez pierwszą głębokość.
VGR

Komparator można poddać recyklingowi, Collections.reverseOrder()aby kod mógł for (Path p : Files.walk(directoryToDelete).sorted(reverseOrder()).toArray(Path[]::new))zakładać, że został zaimportowany statycznie.
namero999,

@ namero999 Masz na myśli Comparator.reverseOrder? Files.walk(dir) .sorted(Comparator.reverseOrder()) .toArray(Path[]::new))
Jeff

@Jeff całkiem pewny masz rację, przeważnie poszło tam przez pamięć :)
namero999

11
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();
}

5
Ulepszona wersja z logiczną wartością zwracaną i bez powielania: pastebin.com/PqJyzQUx
Erik Kaplun

9
static public 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();
    }
}

Niezły kod, ale jest jeden błąd, po naprawieniu działa. Wiersz f.delete()poniżej deleteDirectory(f)wygeneruje wyjątek NoSuchFileException, ponieważ deleteDirectory(f)ten plik został już usunięty. Każdy katalog stanie się ścieżką po przekazaniu deleteDirectory(f)i usunięciu przez path.delete(). Dlatego nie potrzebujemy f.delete()w if f.isDerectorysekcji. Więc po prostu usuń f.delete();w deleteDirectory (f) i to zadziała.
Trieu Nguyen,

5

Dwa sposoby na niepowodzenie z dowiązaniami symbolicznymi i powyższym kodem ... i nie znam rozwiązania.

Sposób nr 1

Uruchom to, aby utworzyć test:

echo test > testfile
mkdir dirtodelete
ln -s badlink dirtodelete/badlinktodelete

Tutaj widzisz swój plik testowy i katalog testowy:

$ ls testfile dirtodelete
testfile

dirtodelete:
linktodelete

Następnie uruchom commons-io deleteDirectory (). Awaria oznacza, że ​​plik nie został znaleziony. Nie jestem pewien, co robią tutaj inne przykłady. Polecenie Linux rm po prostu usunę łącze, a także rm -r w katalogu.

Exception in thread "main" java.io.FileNotFoundException: File does not exist: /tmp/dirtodelete/linktodelete

Sposób nr 2

Uruchom to, aby utworzyć test:

mkdir testdir
echo test > testdir/testfile
mkdir dirtodelete
ln -s ../testdir dirtodelete/dirlinktodelete

Tutaj widzisz swój plik testowy i katalog testowy:

$ ls dirtodelete testdir
dirtodelete:
dirlinktodelete

testdir:
testfile

Następnie uruchom commons-io deleteDirectory () lub przykładowy kod opublikowany przez osoby. Usuwa nie tylko katalog, ale także plik testowy znajdujący się poza katalogiem, który jest usuwany. (Ujawnia katalog domyślnie i usuwa zawartość). rm -r usunie tylko link. Musisz użyć czegoś takiego: usuń dereferencyjne pliki: "find -L dirtodelete -type f -exec rm {} \;".

$ ls dirtodelete testdir
ls: cannot access dirtodelete: No such file or directory
testdir:

4

Możesz użyć:

org.apache.commons.io.FileUtils.deleteQuietly(destFile);

Usuwa plik, nigdy nie zgłaszając wyjątku. Jeśli plik jest katalogiem, usuń go i wszystkie podkatalogi. Różnica między File.delete () a tą metodą jest taka: Katalog do usunięcia nie musi być pusty. Nie można zgłaszać wyjątków, gdy pliku lub katalogu nie można usunąć.


4

Optymalne rozwiązanie, które obsługuje wyjątek spójnie z podejściem, że wyjątek zgłoszony przez metodę powinien zawsze opisywać to, czego ta metoda próbowała (i nie udało się):

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);
    }
}

3

W starszych projektach muszę utworzyć natywny kod Java. Tworzę ten kod podobny do kodu Paulitex. Zobaczyć, że:

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;
   } 
}

I test jednostkowy:

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());
    }

}

3

Poniższy kod rekurencyjnie usuwa całą zawartość w danym folderze.

boolean deleteDirectory(File directoryToBeDeleted) {
    File[] allContents = directoryToBeDeleted.listFiles();
    if (allContents != null) {
        for (File file : allContents) {
            deleteDirectory(file);
        }
    }
    return directoryToBeDeleted.delete();
}

2

Oto główna metoda z odkrytymi kośćmi, która akceptuje argument wiersza poleceń, może być konieczne dołączenie własnego sprawdzania błędów lub ukształtowanie go według własnego uznania.

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();
        }
    }
}

Mam nadzieję że to pomogło!


1

Być może rozwiązaniem tego problemu może być ponowne zaimplementowanie metody usuwania klasy File przy użyciu kodu z odpowiedzi Erickson:

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();
    }
  }
}

1
Myślę, że jest zaimplementowany, ponieważ ma naśladować zachowanie większości narzędzi powłoki poleceń, takich jak „rm”, „rmdir” i „del”. Spośród dwóch alternatyw obecne wdrożenie zdecydowanie minimalizuje ogólny potencjał zaskoczenia (i gniewu). To się nie zmieni.
erickson,

4
Ogólnie rzecz biorąc, jedyne pakiety Java JRE, które widzę jako rozszerzone, pochodzą od Swing. Zwykle rozszerzanie innych klas, takich jak java.io.File, jest złym pomysłem, ponieważ może powodować nieoczekiwane działania.
Eddie

1

Bez Commons IO i <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();
        }

0

Podczas gdy pliki można łatwo usunąć za pomocą file.delete (), katalogi muszą być puste, aby je usunąć. Użyj rekurencji, aby to zrobić z łatwością. Na przykład:

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();
                        }
                    }
                }
            }
        }
    }

0

Kodowałem tę procedurę, która ma 3 kryteria bezpieczeństwa dla bezpieczniejszego użytkowania.

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);
    }
}

0

Załóżmy przykład:

import java.io.File;
import java.io.IOException;

public class DeleteDirectory
{
   private static final String folder = "D:/project/java";

   public static void main(String[] args) throws IOException
   {
      File fl = new File(folder);
      if(!fl.exists()) // checking if directory exists
      {
         System.out.println("Sorry!! directory doesn't exist.");
      }
      else
      {
         DeleteDirectory dd = new DeleteDirectory();
         dd.deleteDirectory(fl);
      }
   }

   public void deleteDirectory(File file) throws IOException
   {
      if(file.isDirectory())
      {
         if(file.list().length == 0)
         { 
            deleteEmptyDirectory(file); // here if directory is empty delete we are deleting
         }
         else
         {
            File fe[] = file.listFiles();
            for(File deleteFile : fe)
            {
               deleteDirectory(deleteFile); // recursive call
            }
            if(file.list().length == 0)
            {
               deleteEmptyDirectory(file);
            }
         }
      }
      else
      {
         file.delete();
         System.out.println("File deleted : " + file.getAbsolutePath());
      }
   }

   private void deleteEmptyDirectory(File fi)
   {
      fi.delete();
      System.out.println("Directory deleted : " + fi.getAbsolutePath());
   }
}

Aby uzyskać więcej informacji, zapoznaj się z poniższymi zasobami

Usuń katalog


0

rm -rfbył znacznie bardziej wydajny niż FileUtils.deleteDirectory.

Po szeroko zakrojonych testach porównawczych stwierdziliśmy, że używanie rm -rfbyło wielokrotnie szybsze niż używanieFileUtils.deleteDirectory .

Oczywiście, jeśli masz mały lub prosty katalog, nie będzie to miało znaczenia, ale w naszym przypadku mieliśmy wiele gigabajtów i głęboko zagnieżdżone podkatalogi, w których zajęłoby to ponad 10 minut FileUtils.deleteDirectoryi tylko 1 minuta rm -rf.

Oto nasza przybliżona implementacja Java, aby to zrobić:

// 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;

}

Warto spróbować, jeśli masz do czynienia z dużymi lub złożonymi katalogami.


To działa na platformie ??
OneCricketeer

@ cricket_007 Które platformy?
Joshua Pinter

Windows? OpenWrt? BSD?
OneCricketeer

1
@ cricket_007 Zdecydowanie nie Windows. Zostało to przetestowane i użyte na Androidzie i MacOS.
Joshua Pinter

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.