Невозможно удалить каталог с помощью Directory.Delete (путь, истина) - PullRequest
359 голосов
/ 30 ноября 2008

Я использую .NET 3.5, пытаюсь рекурсивно удалить каталог, используя:

Directory.Delete(myPath, true);

Насколько я понимаю, это должно произойти, если файлы используются или существует проблема с разрешениями, но в противном случае следует удалить каталог и все его содержимое.

Однако я иногда получаю это:

System.IO.IOException: The directory is not empty.
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
    at System.IO.Directory.DeleteHelper(String fullPath, String userPath, Boolean recursive)
    at System.IO.Directory.Delete(String fullPath, String userPath, Boolean recursive)
    ...

Меня не удивляет, что метод иногда выдает, но я удивляюсь, когда получаю именно это сообщение, когда рекурсивное значение истинно. (Я знаю каталог не пустой.)

Есть ли причина, по которой я вижу это вместо AccessViolationException?

Ответы [ 29 ]

2 голосов
/ 22 сентября 2011

Я потратил несколько часов, чтобы решить эту проблему и другие исключения с удалением каталога. Это мое решение

 public static void DeleteDirectory(string target_dir)
    {
        DeleteDirectoryFiles(target_dir);
        while (Directory.Exists(target_dir))
        {
            lock (_lock)
            {
                DeleteDirectoryDirs(target_dir);
            }
        }
    }

    private static void DeleteDirectoryDirs(string target_dir)
    {
        System.Threading.Thread.Sleep(100);

        if (Directory.Exists(target_dir))
        {

            string[] dirs = Directory.GetDirectories(target_dir);

            if (dirs.Length == 0)
                Directory.Delete(target_dir, false);
            else
                foreach (string dir in dirs)
                    DeleteDirectoryDirs(dir);
        }
    }

    private static void DeleteDirectoryFiles(string target_dir)
    {
        string[] files = Directory.GetFiles(target_dir);
        string[] dirs = Directory.GetDirectories(target_dir);

        foreach (string file in files)
        {
            File.SetAttributes(file, FileAttributes.Normal);
            File.Delete(file);
        }

        foreach (string dir in dirs)
        {
            DeleteDirectoryFiles(dir);
        }
    }

Этот код имеет небольшую задержку, которая не важна для моего приложения. Но будьте осторожны, задержка может быть проблемой для вас, если у вас есть много подкаталогов в каталоге, который вы хотите удалить.

2 голосов
/ 01 мая 2013

Рекурсивное удаление каталогов, которое не удаляет файлы, безусловно, неожиданно. Мое исправление для этого:

public class IOUtils
{
    public static void DeleteDirectory(string directory)
    {
        Directory.GetFiles(directory, "*", SearchOption.AllDirectories).ForEach(File.Delete);
        Directory.Delete(directory, true);
    }
}

Я сталкивался с случаями, когда это помогало, но, как правило, Directory.Delete удаляет файлы в каталогах при рекурсивном удалении, как задокументировано в msdn .

Время от времени я сталкиваюсь с этим нерегулярным поведением также как пользователь Windows Explorer: иногда я не могу удалить папку (она думает, что бессмысленное сообщение «отказано в доступе»), но когда я выполняю детализацию и удаляю нижние элементы, то удалите также верхние элементы. Так что я думаю, что приведенный выше код имеет дело с аномалией ОС, а не с проблемой библиотеки базовых классов.

2 голосов
/ 12 декабря 2017

Ни одно из вышеперечисленных решений не помогло мне. В итоге я использовал отредактированную версию решения @ryascl, как показано ниже:

    /// <summary>
    /// Depth-first recursive delete, with handling for descendant 
    /// directories open in Windows Explorer.
    /// </summary>
    public static void DeleteDirectory(string path)
    {
        foreach (string directory in Directory.GetDirectories(path))
        {
            Thread.Sleep(1);
            DeleteDir(directory);
        }
        DeleteDir(path);
    }

    private static void DeleteDir(string dir)
    {
        try
        {
            Thread.Sleep(1);
            Directory.Delete(dir, true);
        }
        catch (IOException)
        {
            DeleteDir(dir);
        }
        catch (UnauthorizedAccessException)
        {
            DeleteDir(dir);
        }
    }
1 голос
/ 01 декабря 2008

Каталог или файл в нем заблокированы и не могут быть удалены. Найдите виновника, который его запирает, и посмотрите, сможете ли вы его устранить.

1 голос
/ 11 ноября 2016

Эта проблема может возникать в Windows, когда в каталоге (или в любом подкаталоге) есть файлы, длина пути которых превышает 260 символов.

В таких случаях вам нужно удалить \\\\?\C:\mydir вместо C:\mydir. О пределе в 260 символов вы можете прочитать здесь .

1 голос
/ 24 февраля 2010

Похоже, что выбора пути или подпапки в проводнике Windows достаточно, чтобы заблокировать одиночное выполнение Directory.Delete (path, true), выбрасывая IOException, как описано выше, и умирая вместо загрузки Windows Explorer из родительской папки. и действуя, как и ожидалось.

1 голос
/ 29 марта 2011

У меня была эта проблема сегодня. Это происходило потому, что у меня был открыт проводник Windows для каталога, который пытался удалить, вызывая рекурсивный вызов сбой и, следовательно, IOException. Убедитесь, что в каталоге нет ручек.

Кроме того, MSDN ясно, что вам не нужно писать свое собственное мнение: http://msdn.microsoft.com/en-us/library/fxeahc5f.aspx

1 голос
/ 26 ноября 2013

Это из-за FileChangesNotifications.

Это происходит с ASP.NET 2.0. Когда вы удаляете какую-либо папку в приложении, она перезапускается . Вы можете увидеть это сами, используя Мониторинг состояния ASP.NET .

Просто добавьте этот код в ваш web.config / configuration / system.web:

<healthMonitoring enabled="true">
  <rules>
    <add name="MyAppLogEvents" eventName="Application Lifetime Events" provider="EventLogProvider" profile="Critical"/>
  </rules>
</healthMonitoring>


После этого проверьте Windows Log -> Application. Что здесь происходит:

При удалении папки, если есть какая-либо подпапка, Delete(path, true) сначала удаляет подпапку. FileChangesMonitor достаточно знать об удалении и закрыть ваше приложение. Между тем ваш основной каталог еще не удален. Это событие из журнала:

1024 *
*

enter image description here


Delete() не завершил свою работу, и, поскольку приложение закрывается, возникает исключение:

enter image description here

Если у вас нет подпапок в папке, которую вы удаляете, Delete () просто удаляет все файлы и эту папку, приложение тоже перезапускается, но вы не получаете любые исключения , потому что перезапуск приложения ничего не прерывает. Но все равно вы теряете все внутрипроцессные сеансы, приложение не отвечает на запросы при перезапуске и т. Д.

Что теперь?

Существуют некоторые обходные пути и настройки, чтобы отключить это поведение, Соединение каталогов , Отключение FCN с реестром , Остановка FileChangesMonitor с помощью Reflection (так как не существует открытого метода) , но все они, похоже, не правы, потому что там есть причина по FCN. Он следит за структурой вашего приложения , которая не является структурой ваших данных . Краткий ответ: разместите папки, которые вы хотите удалить за пределами вашего приложения. FileChangesMonitor не будет получать уведомлений, и ваше приложение не будет перезапускаться каждый раз. Вы не получите никаких исключений. Чтобы сделать их видимыми из Интернета, есть два способа:

  1. Создайте контроллер, который обрабатывает входящие вызовы, а затем отправляет файлы обратно, читая из папки вне приложения (за пределами wwwroot).

  2. Если ваш проект большой и производительность важнее всего, установите отдельный маленький и быстрый веб-сервер для обслуживания статического контента. Таким образом, вы оставите IIS его конкретную работу. Это может быть на той же машине (mongoose для Windows) или на другой машине (nginx для Linux). Хорошей новостью является то, что вам не нужно платить дополнительную лицензию Microsoft для установки статического сервера контента в Linux.

Надеюсь, это поможет.

1 голос
/ 24 июля 2013

У меня была такая же проблема с Windows Workflow Foundation на сервере сборки с TFS2012. Внутренне рабочий процесс называется Directory.Delete () с рекурсивным флагом, установленным в true. В нашем случае это связано с сетью.

Мы удаляли двоичную папку для отбрасывания на общем сетевом ресурсе, прежде чем заново создавать и заново заполнять ее последними двоичными файлами. Любая другая сборка потерпит неудачу. При открытии папки удаления после неудачной сборки папка была пустой, что указывает на то, что все аспекты вызова Directory.Delete () были успешными, за исключением удаления действительного каталога.

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

Два возможных решения в нашем случае:

  • Создайте рекурсивное удаление в нашем собственном коде с задержками и проверками между каждым шагом
  • Повторите попытку до X раз после IOException, давая задержку перед повторной попыткой

Последний метод быстрый и грязный, но, похоже, сработает.

1 голос
/ 01 декабря 2008

Возможно ли, что у вас есть состояние гонки, когда другой поток или процесс добавляет файлы в каталог:

Последовательность будет:

Процесс удаления A:

  1. Очистить каталог
  2. Удалить (теперь пустой) каталог.

Если кто-то еще добавит файл между 1 и 2, то, возможно, 2 выдаст исключение из списка?

...