Непустой каталог не всегда удаляется при открытии в проводнике - PullRequest
1 голос
/ 18 октября 2019

Я пытаюсь найти способ удалить непустую директорию. Но все решения, которые я нашел, имели общую проблему - они не полностью удаляют непустую папку при определенных обстоятельствах, например, если она открыта в проводнике.

Эта проблема возникает не только при открытии в проводнике, нотакже в других случаях, которые я пока не смог определить. Но дело в том, что удаление не всегда работает правильно. Кажется, что Java не успевает обновить информацию каталога и думает, что папка не пуста, хотя все файлы в ней уже были удалены, потому что если выполнить код в режиме отладки или заставить поток засыпать хотя быЧерез 1 мс после каждого удаления папка всегда удаляется правильно.

Вот некоторые решения, которые я нашел:

// Using java.io.File
public static boolean deleteDirectory(File path) {
    if (path.exists()) {
        File[] files = path.listFiles();
        if (files != null) {
            for (File file : files) {
                deleteDirectory(file);
            }
        }
        Path p = path.toPath();
        try {
            Files.delete(p);
            return true;
        }
        catch (IOException ex) {
            ex.printStackTrace();
            return false;
        }
    }

    return true;
}

// Using java.nio.file.*
public static boolean deleteDirectory(Path path) {
    try {
        Files.walk(path)
            .sorted(Comparator.reverseOrder())
            .map(Path::toFile)
            .forEach(file -> {
                if (!file.delete()) throw new DeleteIOException(file);
            });
    }
    catch (IOException ex) {
        log.error("Ошибка удаления директории: " + path.toAbsolutePath(), ex);
        return false;
    }
    catch (DeleteIOException ex) {
        return false;
    }
    return true;
}

// Using NIO2 Java 7
public static boolean deleteDirectoryWalkTree(Path path) {
    FileVisitor<Path> visitor = 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 {
            Files.delete(file);
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            if (exc != null) {
                throw exc;
            }
            Files.delete(dir);
            return FileVisitResult.CONTINUE;
        }
    };
    try {
        Files.walkFileTree(path, visitor);
    }
    catch (IOException e) {
        e.printStackTrace();
        return false;
    }
    return true;
}

// Using FileUtils from commons-io
public static boolean deleteDirectoryCommonsIO(File path) {
    try {
        FileUtils.deleteDirectory(path);
    }
    catch (IOException e) {
        e.printStackTrace();
        return false;
    }
    return true;
}

// Using FileSystemUtils from Spring
public static boolean deleteDirectorySpring(Path path) {
    try {
        return FileSystemUtils.deleteRecursively(path);
    }
    catch (IOException e) {
        e.printStackTrace();
        return false;
    }
}

Я использовал этот код для тестирования и воспроизведения проблемы. Я установил точку останова перед удалением и открыл в проводнике "BASE_TEST_DIRECTORY / 1 / s1". Все тесты не пройдены.

@BeforeEach
private void createAndTestDirectories() throws IOException {
    String dir1 = "1";
    String dir2 = "2";
    String dir3 = "3";
    String subDir1 = "s1";
    String subDir2 = "s2";
    String subDir3 = "s3";
    String fileName = "file";
    createDirectory(Paths.get(BASE_TEST_DIRECTORY));
    createDirectory(Paths.get(BASE_TEST_DIRECTORY, dir1));
    createDirectory(Paths.get(BASE_TEST_DIRECTORY, dir2));
    createDirectory(Paths.get(BASE_TEST_DIRECTORY, dir3));
    createDirectory(Paths.get(BASE_TEST_DIRECTORY, dir1, subDir1));
    createDirectory(Paths.get(BASE_TEST_DIRECTORY, dir1, subDir2));
    createDirectory(Paths.get(BASE_TEST_DIRECTORY, dir1, subDir3));
    createFile(Paths.get(BASE_TEST_DIRECTORY, dir1, subDir1, fileName));

    assertTrue(Files.exists(Paths.get(BASE_TEST_DIRECTORY)));
    assertTrue(Files.exists(Paths.get(BASE_TEST_DIRECTORY, dir1, subDir1, fileName)));
}

@Test
public void deleteDirectoryPath_ExpectCleanDirectory() {
    boolean result = DeleteDirectoryUtil.deleteDirectory(Paths.get(BASE_TEST_DIRECTORY));

    assertTrue(result);
    assertFalse(Files.exists(Paths.get(BASE_TEST_DIRECTORY)));
}

У меня есть такие ошибки:

java.nio.file.DirectoryNotEmptyException: target\ioTests\1
...

java.io.IOException: Unable to delete directory target\ioTests\1.
...

Любые предложения, чтобы решить эту проблему и не использовать Thread.sleep (1) ?

UPD

Проблема появляется в Windows 10. Я не тестировал на других ОС.

Проблема появляется не только если папка открыта в Проводнике,описанный случай с проводником - это самый простой способ воспроизвести его.

Иногда удаление не работает полностью во время нормального выполнения программы, когда в папке не происходит никаких процессов, поэтому я и задал этот вопрос. Иногда он полностью удаляется, а иногда нет.

Эта проблема возникает не только из-за проводника Windows, поскольку при удалении папки, в которой он находится, он удаляется успешно, и проводник пытается отобразить предыдущую папку, как будтоВы нажали Alt + Стрелка влево или Backspace. А также, как я описал выше, удаление работает нормально, даже если папка открыта в проводнике, если вы последовательно выполняете код в режиме отладки или вставляете этот код после каждого удаления:

try {
    Thread.sleep(1);
}
catch (InterruptedException e) {
    e.printStackTrace();
}

Другие решенияfrom org.apache.commons.io ( FileUtils.forceDelete (каталог) и FileDeleteStrategy.FORCE.delete (aFile) ), которые @Yasham, предложенные в комментариях, имеют ту же проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...