Невозможно удалить каталог с помощью Directory.Delete(путь, истина)
Я использую.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?
32 ответа
Примечание редактора: хотя этот ответ содержит некоторую полезную информацию, на самом деле он неверен Directory.Delete
, Пожалуйста, прочитайте комментарии к этому ответу и другие ответы на этот вопрос.
Я столкнулся с этой проблемой раньше.
Корень проблемы в том, что эта функция не удаляет файлы, которые находятся в структуре каталогов. Итак, вам нужно создать функцию, которая удаляет все файлы в структуре каталогов, а затем все каталоги, прежде чем удалять сам каталог. Я знаю, что это идет против второго параметра, но это гораздо более безопасный подход. Кроме того, вы, вероятно, захотите удалить атрибуты доступа READ-ONLY из файлов непосредственно перед их удалением. В противном случае это вызовет исключение.
Просто вставьте этот код в свой проект.
public static void DeleteDirectory(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)
{
DeleteDirectory(dir);
}
Directory.Delete(target_dir, false);
}
Кроме того, я лично добавляю ограничение на области машины, которые разрешено удалять, потому что вы хотите, чтобы кто-то вызывал эту функцию на C:\WINDOWS (%WinDir%)
или же C:\
,
Если вы пытаетесь рекурсивно удалить каталог a
и каталог a\b
открыт в проводнике, b
будет удален, но вы получите сообщение об ошибке "каталог не пустой" для a
даже если он пуст, когда вы идете и смотрите. Текущий каталог любого приложения (включая Проводник) сохраняет дескриптор каталога. Когда вы звоните Directory.Delete(true)
удаляет снизу вверх: b
, затем a
, Если b
открыт в проводнике, Explorer обнаружит удаление b
изменить каталог вверх cd ..
и убирать открытые ручки. Поскольку файловая система работает асинхронно, Directory.Delete
Сбой операции из-за конфликтов с Explorer.
Неполное решение
Первоначально я опубликовал следующее решение с идеей прерывания текущего потока, чтобы у проводника было время освободить дескриптор каталога.
// incomplete!
try
{
Directory.Delete(path, true);
}
catch (IOException)
{
Thread.Sleep(0);
Directory.Delete(path, true);
}
Но это работает только в том случае, если открытый каталог является непосредственным дочерним элементом удаляемого каталога. Если a\b\c\d
открыт в проводнике, и вы используете это на a
эта техника не удастся после удаления d
а также c
,
Несколько лучшее решение
Этот метод будет обрабатывать удаление глубокой структуры каталогов, даже если в Проводнике открыт один из каталогов нижнего уровня.
/// <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))
{
DeleteDirectory(directory);
}
try
{
Directory.Delete(path, true);
}
catch (IOException)
{
Directory.Delete(path, true);
}
catch (UnauthorizedAccessException)
{
Directory.Delete(path, true);
}
}
Несмотря на дополнительную работу по рекурсивной работе самостоятельно, нам все еще приходится справляться с UnauthorizedAccessException
это может произойти по пути. Неясно, прокладывает ли первая попытка удаления вторую, удачную, или это просто временная задержка, вызванная выбросом / перехватом исключения, которое позволяет файловой системе наверстать упущенное.
Вы можете уменьшить количество исключений, возникающих и возникающих в типичных условиях, добавив Thread.Sleep(0)
в начале try
блок. Кроме того, существует риск, что при большой нагрузке на систему вы можете пролететь через оба Directory.Delete
попытки и неудачи. Считайте это решение отправной точкой для более надежного рекурсивного удаления.
Общий ответ
Это решение касается только особенностей взаимодействия с Windows Explorer. Если вам нужна надежная операция удаления, следует помнить, что все (антивирусный сканер и т. Д.) Может иметь открытый дескриптор того, что вы пытаетесь удалить, в любое время. Поэтому вы должны попробовать позже. Сколько позже и сколько раз вы попробуете, зависит от того, насколько важно, чтобы объект был удален. Как указывает MSDN,
Надежный файловый итерационный код должен учитывать многие сложности файловой системы.
Это невинное утверждение, снабженное только ссылкой на справочную документацию NTFS, должно заставить ваши волосы встать.
(Изменить: много. Первоначально у этого ответа было только первое, неполное решение.)
Прежде чем идти дальше, проверьте следующие причины, которые находятся под вашим контролем:
- Папка установлена как текущая директория вашего процесса? Если да, сначала измените его на что-то другое.
- Вы открыли файл (или загрузили DLL) из этой папки? (и забыл закрыть / разгрузить его)
В противном случае проверьте следующие законные причины вне вашего контроля:
- В этой папке есть файлы, помеченные как доступные только для чтения.
- У вас нет разрешения на удаление некоторых из этих файлов.
- Файл или подпапка открыта в Проводнике или другом приложении.
Если что-то из перечисленного является проблемой, вы должны понять, почему это происходит, прежде чем пытаться улучшить свой код удаления. Должно ли ваше приложение удалять файлы только для чтения или недоступные файлы? Кто так их пометил и почему?
Как только вы исключите вышеуказанные причины, все еще существует вероятность ложных сбоев. Удаление не удастся, если кто-то держит дескриптор любого из удаляемых файлов или папок, и существует множество причин, по которым кто-то может перечислять папку или читать ее файлы:
- поисковые индексы
- антивирусы
- программное обеспечение для резервного копирования
Общий подход к устранению ложных сбоев состоит в том, чтобы делать попытки несколько раз, делая паузу между попытками. Очевидно, что вы не хотите продолжать пытаться вечно, поэтому вам следует отказаться после определенного количества попыток и либо выбросить исключение, либо проигнорировать ошибку. Как это:
private static void DeleteRecursivelyWithMagicDust(string destinationDir) {
const int magicDust = 10;
for (var gnomes = 1; gnomes <= magicDust; gnomes++) {
try {
Directory.Delete(destinationDir, true);
} catch (DirectoryNotFoundException) {
return; // good!
} catch (IOException) { // System.IO.IOException: The directory is not empty
System.Diagnostics.Debug.WriteLine("Gnomes prevent deletion of {0}! Applying magic dust, attempt #{1}.", destinationDir, gnomes);
// see http://stackru.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true for more magic
Thread.Sleep(50);
continue;
}
return;
}
// depending on your use case, consider throwing an exception here
}
На мой взгляд, такой помощник должен использоваться для всех удалений, потому что ложные сбои всегда возможны. Однако ВЫ ДОЛЖНЫ ПРИНЯТЬ ЭТОТ КОД К ВАШЕМУ ИСПОЛЬЗОВАНИЮ, а не просто слепо копировать его.
У меня были ложные сбои для внутренней папки данных, созданной моим приложением, расположенной в папке%LocalAppData%, поэтому мой анализ выглядит следующим образом:
Папка управляется исключительно моим приложением, и у пользователя нет веской причины для того, чтобы отмечать вещи как доступные только для чтения или недоступные в этой папке, поэтому я не пытаюсь разобраться с этим случаем.
Там нет ценных созданных пользователем вещей, поэтому нет риска принудительного удаления чего-либо по ошибке.
Будучи внутренней папкой данных, я не ожидаю, что она будет открыта в проводнике, по крайней мере, я не чувствую необходимости конкретно обрабатывать дело (т.е. я прекрасно справляюсь с этим делом через службу поддержки).
Если все попытки не удаются, я предпочитаю игнорировать ошибку. В худшем случае приложение не может распаковать некоторые новые ресурсы, вылетает и предлагает пользователю обратиться в службу поддержки, что является приемлемым для меня, если это происходит не часто. Или, если приложение не аварийно завершает работу, оно оставляет некоторые старые данные, что снова приемлемо для меня.
Я решил ограничить количество попыток до 500 мс (50 * 10). Это произвольный порог, который работает на практике; Я хотел, чтобы порог был достаточно коротким, чтобы пользователи не убивали приложение, думая, что оно перестало отвечать. С другой стороны, полсекунды - это достаточно времени, чтобы преступник мог закончить обработку моей папки. Судя по другим SO ответам, которые иногда находят даже
Sleep(0)
чтобы быть приемлемым, очень немногие пользователи когда-либо будут испытывать больше, чем одна повторная попытка.Я повторяю каждые 50 мс, что является другим произвольным числом. Я чувствую, что если файл обрабатывается (индексируется, проверяется), когда я пытаюсь его удалить, 50 мс - это подходящее время, чтобы ожидать завершения обработки в моем случае. Кроме того, 50 мс достаточно мал, чтобы не привести к заметному замедлению; снова,
Sleep(0)
Кажется, этого достаточно во многих случаях, поэтому мы не хотим слишком долго откладывать.Код повторяется при любых исключениях ввода-вывода. Обычно я не ожидаю каких-либо исключений при доступе к%LocalAppData%, поэтому я выбрал простоту и принял риск задержки в 500 мс в случае легитимного исключения. Я также не хотел найти способ обнаружить точное исключение, к которому я хочу повторить попытку.
Современный асинхронный ответ
Принятый ответ просто неверен, он может работать для некоторых людей, потому что время, необходимое для получения файлов с диска, освобождает все, что блокировало файлы. Дело в том, что это происходит потому, что файлы блокируются каким-либо другим процессом / потоком / действием. Другие ответы используют Thread.Sleep
(Yuck), чтобы повторить попытку удаления каталога через некоторое время. Этот вопрос нуждается в повторении с более современным ответом.
public static async Task<bool> TryDeleteDirectory(
string directoryPath,
int maxRetries = 10,
int millisecondsDelay = 30)
{
if (directoryPath == null)
throw new ArgumentNullException(directoryPath);
if (maxRetries < 1)
throw new ArgumentOutOfRangeException(nameof(maxRetries));
if (millisecondsDelay < 1)
throw new ArgumentOutOfRangeException(nameof(millisecondsDelay));
for (int i = 0; i < maxRetries; ++i)
{
try
{
if (Directory.Exists(directoryPath))
{
Directory.Delete(directoryPath, true);
}
return true;
}
catch (IOException)
{
await Task.Delay(millisecondsDelay);
}
catch (UnauthorizedAccessException)
{
await Task.Delay(millisecondsDelay);
}
}
return false;
}
Модульные тесты
Эти тесты показывают пример того, как заблокированный файл может вызвать Directory.Delete
потерпеть неудачу и как TryDeleteDirectory
Метод выше решает проблему.
[Fact]
public async Task TryDeleteDirectory_FileLocked_DirectoryNotDeletedReturnsFalse()
{
var directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
var subDirectoryPath = Path.Combine(Path.GetTempPath(), "SubDirectory");
var filePath = Path.Combine(directoryPath, "File.txt");
try
{
Directory.CreateDirectory(directoryPath);
Directory.CreateDirectory(subDirectoryPath);
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
{
var result = await TryDeleteDirectory(directoryPath, 3, 30);
Assert.False(result);
Assert.True(Directory.Exists(directoryPath));
}
}
finally
{
if (Directory.Exists(directoryPath))
{
Directory.Delete(directoryPath, true);
}
}
}
[Fact]
public async Task TryDeleteDirectory_FileLockedThenReleased_DirectoryDeletedReturnsTrue()
{
var directoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
var subDirectoryPath = Path.Combine(Path.GetTempPath(), "SubDirectory");
var filePath = Path.Combine(directoryPath, "File.txt");
try
{
Directory.CreateDirectory(directoryPath);
Directory.CreateDirectory(subDirectoryPath);
Task<bool> task;
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
{
task = TryDeleteDirectory(directoryPath, 3, 30);
await Task.Delay(30);
Assert.True(Directory.Exists(directoryPath));
}
var result = await task;
Assert.True(result);
Assert.False(Directory.Exists(directoryPath));
}
finally
{
if (Directory.Exists(directoryPath))
{
Directory.Delete(directoryPath, true);
}
}
}
Одна важная вещь, о которой следует упомянуть (я добавил ее в качестве комментария, но мне не разрешено), это то, что поведение перегрузки изменилось с.NET 3.5 на.NET 4.0.
Directory.Delete(myPath, true);
Начиная с.NET 4.0 он удаляет файлы в самой папке, но НЕ в 3.5. Это можно увидеть и в документации MSDN.
.NET 4.0
Удаляет указанный каталог и, если указано, любые подкаталоги и файлы в каталоге.
.NET 3.5
Удаляет пустой каталог и, если указано, любые подкаталоги и файлы в каталоге.
У меня была та же самая проблема под Delphi. И в результате мое собственное приложение блокировало каталог, который я хотел удалить. Каким-то образом каталог был заблокирован, когда я писал в него (некоторые временные файлы).
Уловка 22 состояла в том, что я сделал простую замену каталога на его родительский, прежде чем удалить его.
Вы можете воспроизвести ошибку, запустив:
Directory.CreateDirectory(@"C:\Temp\a\b\c\");
Process.Start(@"C:\Temp\a\b\c\");
Thread.Sleep(1000);
Directory.Delete(@"C:\Temp\a\b\c");
Directory.Delete(@"C:\Temp\a\b");
Directory.Delete(@"C:\Temp\a");
При попытке удалить каталог 'b' выдает исключение IOException "Каталог не пустой". Это глупо, так как мы только что удалили каталог 'c'.
Насколько я понимаю, объяснение состоит в том, что каталог "c" помечается как удаленный. Но удаление еще не зафиксировано в системе. Система ответила, что работа выполнена, хотя на самом деле она все еще обрабатывается. Система, вероятно, ожидает, что файловый менеджер сосредоточится на родительском каталоге, чтобы зафиксировать удаление.
Если вы посмотрите на исходный код функции Delete ( http://referencesource.microsoft.com/), то увидите, что она использует встроенную функцию Win32Native.RemoveDirectory. Это поведение "не ждать" отмечено здесь:
Функция RemoveDirectory помечает каталог для удаления при закрытии. Следовательно, каталог не удаляется до тех пор, пока последний дескриптор каталога не будет закрыт.
( http://msdn.microsoft.com/en-us/library/windows/desktop/aa365488(v=vs.85).aspx)
Сон и повторите это решение. См. Решение Ряскля.
Я удивлен, что никто не задумывался об этом простом нерекурсивном методе, который может удалять каталоги, содержащие файлы только для чтения, без необходимости изменять атрибут "только чтение" каждого из них.
Process.Start("cmd.exe", "/c " + @"rmdir /s/q C:\Test\TestDirectoryContainingReadOnlyFiles");
(Измените немного, чтобы не запускать окно cmd на мгновение, которое доступно по всему интернету)
У меня были странные проблемы с разрешением на удаление каталогов профилей пользователей (в C:\Documents and Settings), несмотря на то, что я мог это сделать в оболочке Explorer.
File.SetAttributes(target_dir, FileAttributes.Normal);
Directory.Delete(target_dir, false);
Мне не имеет смысла, что "файловая" операция делает с каталогом, но я знаю, что она работает, и этого мне достаточно!
Ни одно из перечисленных выше решений не помогло мне. В итоге я использовал отредактированную версию решения @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);
}
}
Этот ответ основан на: /questions/43284045/nevozmozhno-udalit-katalog-s-pomoschyu-directorydeleteput-istina/43284051#43284051. Разница с моим кодом заключается в том, что мы возвращаем много удаленных подкаталогов и файлов только при необходимости, вызов Directory.Delete завершается неудачно с первой попытки (что может произойти из-за того, что проводник Windows просматривает каталог).
public static void DeleteDirectory(string dir, bool secondAttempt = false)
{
// If this is a second try, we are going to manually
// delete the files and sub-directories.
if (secondAttempt)
{
// Interrupt the current thread to allow Explorer time to release a directory handle
Thread.Sleep(0);
// Delete any files in the directory
foreach (var f in Directory.GetFiles(dir, "*.*", SearchOption.TopDirectoryOnly))
File.Delete(f);
// Try manually recursing and deleting sub-directories
foreach (var d in Directory.GetDirectories(dir))
DeleteDirectory(d);
// Now we try to delete the current directory
Directory.Delete(dir, false);
return;
}
try
{
// First attempt: use the standard MSDN approach.
// This will throw an exception a directory is open in explorer
Directory.Delete(dir, true);
}
catch (IOException)
{
// Try again to delete the directory manually recursing.
DeleteDirectory(dir, true);
}
catch (UnauthorizedAccessException)
{
// Try again to delete the directory manually recursing.
DeleteDirectory(dir, true);
}
}
Рекурсивное удаление каталогов, которое не удаляет файлы, безусловно, неожиданно. Мое исправление для этого:
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: иногда я не могу удалить папку (она думает, что бессмысленное сообщение "отказано в доступе"), но когда я развертываю и удаляю нижние элементы, я могу удалить верхнюю предметы также. Поэтому я думаю, что приведенный выше код имеет дело с аномалией ОС, а не с проблемой библиотеки базовых классов.
Как упомянуто выше, "принятое" решение терпит неудачу в точках повторного анализа - но люди все еще отмечают его (???). Существует гораздо более короткое решение, которое правильно копирует функциональность:
public static void rmdir(string target, bool recursive)
{
string tfilename = Path.GetDirectoryName(target) +
(target.Contains(Path.DirectorySeparatorChar.ToString()) ? Path.DirectorySeparatorChar.ToString() : string.Empty) +
Path.GetRandomFileName();
Directory.Move(target, tfilename);
Directory.Delete(tfilename, recursive);
}
Я знаю, что не обрабатывает случаи разрешений, упомянутые позже, но для всех целей и задач FAR BETTER предоставляет ожидаемую функциональность оригинального / стокового Directory.Delete () - и с гораздо меньшим количеством кода тоже.
Вы можете безопасно продолжить обработку, потому что старый каталог будет в стороне … даже если он не исчезнет, потому что "файловая система все еще догоняет" (или любое другое оправдание, которое MS дал для предоставления сломанной функции).
В качестве преимущества, если вы знаете, что ваш целевой каталог большой / глубокий и не хотите ждать (или беспокоиться об исключениях), последнюю строку можно заменить на:
ThreadPool.QueueUserWorkItem((o) => { Directory.Delete(tfilename, recursive); });
Вы все еще можете продолжать работать.
Возможно, у вас есть состояние гонки, когда другой поток или процесс добавляет файлы в каталог:
Последовательность будет:
Процесс удаления А:
- Очистить каталог
- Удалить (теперь пустой) каталог.
Если кто-то еще добавит файл между 1 и 2, то, возможно, 2 выдаст исключение из списка?
Вам не нужно создавать дополнительный метод для рекурсивности или удалять файлы внутри дополнительной папки. Все это происходит автоматически путем вызова
DirectoryInfo.Delete();
Подробности здесь.
Что-то вроде этого работает неплохо:
var directoryInfo = new DirectoryInfo("My directory path");
// Delete all files from app data directory.
foreach (var subDirectory in directoryInfo.GetDirectories())
{
subDirectory.Delete(true);// true set recursive paramter, when it is true delete sub file and sub folder with files too
}
передача true в качестве переменной для метода удаления также удалит подфайлы и подпапку с файлами.
Я потратил несколько часов, чтобы решить эту проблему и другие исключения с удалением каталога. Это мое решение
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);
}
}
Этот код имеет небольшую задержку, которая не важна для моего приложения. Но будьте осторожны, задержка может быть проблемой для вас, если у вас есть много подкаталогов в каталоге, который вы хотите удалить.
У меня была такая же проблема с Windows Workflow Foundation на сервере сборки с TFS2012. Внутренне, рабочий процесс называется Directory.Delete() с рекурсивным флагом, установленным в true. В нашем случае это связано с сетью.
Мы удаляли двоичную папку отбрасывания на общем сетевом ресурсе, а затем заново создавали и заново заполняли ее последними двоичными файлами. Любая другая сборка потерпит неудачу. При открытии папки удаления после неудачной сборки папка была пустой, что указывает на то, что все аспекты вызова Directory.Delete() были успешными, за исключением удаления действительного каталога.
Эта проблема, по-видимому, вызвана асинхронным характером передачи файлов по сети. Сервер сборки приказал файловому серверу удалить все файлы, а файловый сервер сообщил, что это произошло, хотя он еще не был полностью завершен. Затем сервер сборки запросил удаление каталога, а файловый сервер отклонил запрос, поскольку он не завершил удаление файлов.
Два возможных решения в нашем случае:
- Создайте рекурсивное удаление в нашем собственном коде с задержками и проверками между каждым шагом
- Повторите попытку до X раз после IOException, давая задержку перед повторной попыткой
Последний метод быстрый и грязный, но, кажется, делает свое дело.
Это из-за FileChangesNotifications.
Это происходит начиная с ASP.NET 2.0. Когда вы удаляете какую-либо папку в приложении, она перезапускается. Вы можете увидеть это самостоятельно, используя ASP.NET Health Monitoring.
Просто добавьте этот код в ваш 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 достаточно знать об удалении и закрытии вашего приложения. Между тем ваш основной каталог еще не удален. Это событие из журнала:
Delete()
не завершил свою работу, и, поскольку приложение закрывается, возникает исключение:
Если у вас нет подпапок в папке, которую вы удаляете, Delete() просто удаляет все файлы и эту папку, приложение тоже перезапускается, но вы не получаете никаких исключений, потому что перезапуск приложения ничего не прерывает. Но все равно вы теряете все внутрипроцессные сеансы, приложение не отвечает на запросы при перезапуске и т. Д.
Что теперь?
Существуют некоторые обходные пути и трюки для отключения этого поведения, " Соединение каталогов", " Отключение FCN с реестром", " Остановка FileChangesMonitor с помощью Reflection" (поскольку нет открытого метода), но все они, похоже, не правы, потому что FCN существует для причина. Он следит за структурой вашего приложения, которая не является структурой ваших данных. Краткий ответ: разместите папки, которые вы хотите удалить за пределами вашего приложения. FileChangesMonitor не будет получать уведомлений, и ваше приложение не будет перезапускаться каждый раз. Вы не получите никаких исключений. Чтобы сделать их видимыми из Интернета, есть два способа:
Создайте контроллер, который обрабатывает входящие вызовы, а затем отправляет файлы обратно, читая из папки вне приложения (вне wwwroot).
Если ваш проект большой и производительность важнее всего, создайте отдельный маленький и быстрый веб-сервер для обслуживания статического контента. Таким образом, вы оставите IIS свою конкретную работу. Это может быть на той же машине (mongoose для Windows) или на другой машине (nginx для Linux). Хорошей новостью является то, что вам не нужно платить дополнительную лицензию Microsoft для установки статического сервера контента в Linux.
Надеюсь это поможет.
Я решил с этой техникой тысячелетия (вы можете оставить Thread.Sleep самостоятельно в улове)
bool deleted = false;
do
{
try
{
Directory.Delete(rutaFinal, true);
deleted = true;
}
catch (Exception e)
{
string mensaje = e.Message;
if( mensaje == "The directory is not empty.")
Thread.Sleep(50);
}
} while (deleted == false);
Похоже, что выбор пути или подпапки, выбранных в проводнике Windows, достаточен, чтобы заблокировать одиночное выполнение Directory.Delete(path, true), выбрасывая IOException, как описано выше, и умирая вместо загрузки Windows Explorer из родительской папки и действуя как ожидается.
У меня была эта проблема сегодня. Это происходило потому, что у меня проводник Windows был открыт в каталог, который пытался удалить, что вызвало рекурсивный вызов сбой и, следовательно, IOException. Убедитесь, что в каталоге нет ручек.
Кроме того, в MSDN ясно, что вам не нужно писать собственный отзыв: http://msdn.microsoft.com/en-us/library/fxeahc5f.aspx
Эта проблема может возникать в Windows, когда в каталоге (или в любом подкаталоге) есть файлы, длина пути которых превышает 260 символов.
В таких случаях вам нужно удалить \\\\?\C:\mydir
вместо C:\mydir
, О пределе в 260 символов вы можете прочитать здесь.
Каталог или файл в нем заблокированы и не могут быть удалены. Найдите виновника, который его запирает, и посмотрите, сможете ли вы его устранить.
Я решил один возможный случай указанной проблемы, когда методы были асинхронными и закодированы следующим образом:
// delete any existing update content folder for this update
if (await fileHelper.DirectoryExistsAsync(currentUpdateFolderPath))
await fileHelper.DeleteDirectoryAsync(currentUpdateFolderPath);
С этим:
bool exists = false;
if (await fileHelper.DirectoryExistsAsync(currentUpdateFolderPath))
exists = true;
// delete any existing update content folder for this update
if (exists)
await fileHelper.DeleteDirectoryAsync(currentUpdateFolderPath);
Заключение? Существует некоторый асинхронный аспект избавления от дескриптора, используемого для проверки существования, с которым Microsoft не может говорить. Это как если бы асинхронный метод внутри оператора if имел оператор if, действующий как оператор using.
Я думаю, что есть файл, открытый каким-то потоком, о котором вы не знаете, у меня была та же проблема, и я решил ее, закрыв все потоки, которые указывали на каталог, который я хотел удалить.
В дополнение ко многим исчерпывающим ответам здесь о проводнике Windows и файловых дескрипторах я столкнулся с DirectoryNotFoundException, когда у меня был слишком длинный путь . По какой-то причине наш код распаковки может создавать пути длиной 270 символов. Это больше, чем максимум 260 в моем случае. Когда я пытаюсь удалить его с помощьюDirectory.Delete(folder, recursive: true)
, он говорит мне, что не может найти каталог.
Изменить: это не вызвало бы проблему OP, потому что ошибка не является прерывистой - она выдает ошибку при каждой попытке.
Эта ошибка возникает, если какой-либо файл или каталог считается используемым. Это вводящая в заблуждение ошибка. Проверьте, открыты ли у вас какие-либо окна проводника или окна командной строки для какого-либо каталога в дереве или для программы, которая использует файл в этом дереве.
Если текущий каталог вашего приложения (или любого другого приложения) является тем, который вы пытаетесь удалить, это не будет ошибкой нарушения прав доступа, но каталог не будет пустым. Убедитесь, что это не ваше собственное приложение, изменив текущий каталог; также убедитесь, что каталог не открыт в какой-либо другой программе (например, Word, Excel, Total Commander и т. д.). Большинство программ перейдет в каталог последнего открытого файла, что может привести к этому.
В случае сетевых файлов Directory.DeleteHelper(recursive:=true) может вызвать IOException, что вызвано задержкой удаления файла
Я решил проблему таким образом:
foreach (DirectoryInfo directoryInfo in directory.GetDirectories())
{
var creationDate = DateTime.Now - directoryInfo.LastWriteTime;
while (true)
{
var files = Directory.GetFiles(directoryInfo.FullName);
if (files.Length == 0)
{
break;
}
Operations.ShellAdmin("DEL *.* /Q", directoryInfo.FullName);
}
directoryInfo.Delete();
}