Как удалить каталог с файлами только для чтения в C#?
мне нужно удалить каталог, содержащий файлы только для чтения. Какой подход лучше:
используя
DirectoryInfo.Delete()
илиManagementObject.InvokeMethod("Delete")
?
С DirectoryInfo.Delete()
, Я должен вручную отключить атрибут только для чтения для каждого файла, но ManagementObject.InvokeMethod("Delete")
не нужно. Есть ли ситуация, когда одно предпочтительнее другого?
пример кода (тест.txt только для чтения).
первый образом:
DirectoryInfo dir = new DirectoryInfo(@"C:UsersDavidDesktop");
dir.CreateSubdirectory("Test");
DirectoryInfo test = new DirectoryInfo(@"C:UsersDavidDesktopTest");
File.Copy(@"C:UsersDavidDesktoptest.txt", @"C:UsersDavidDesktopTesttest.txt");
File.SetAttributes(@"C:UsersDavidDesktopTesttest.txt", FileAttributes.Archive);
test.Delete(true);
второй вариант:
DirectoryInfo dir = new DirectoryInfo(@"C:UsersDavidDesktop");
dir.CreateSubdirectory("Test");
DirectoryInfo test = new DirectoryInfo(@"C:UsersDavidDesktopTest");
File.Copy(@"C:UsersDavidDesktoptest.txt", @"C:UsersDavidDesktopTesttest.txt");
string folder = @"C:UsersDavidDesktopTest";
string dirObject = "Win32_Directory.Name='" + folder + "'";
using (ManagementObject managementObject = new ManagementObject(dirObject))
{
managementObject.Get();
ManagementBaseObject outParams = managementObject.InvokeMethod("Delete", null,
null);
// ReturnValue should be 0, else failure
if (Convert.ToInt32(outParams.Properties["ReturnValue"].Value) != 0)
{
}
}
11 ответов:
вот метод расширения, который устанавливает
Attributes
toNormal
рекурсивно, затем удаляет элементы:public static void DeleteReadOnly(this FileSystemInfo fileSystemInfo) { var directoryInfo = fileSystemInfo as DirectoryInfo; if (directoryInfo != null) { foreach (FileSystemInfo childInfo in directoryInfo.GetFileSystemInfos()) { childInfo.DeleteReadOnly(); } } fileSystemInfo.Attributes = FileAttributes.Normal; fileSystemInfo.Delete(); }
самый простой способ избежать рекурсивных вызовов-это использовать при получении
FileSystemInfo
s, Вот так:public static void ForceDeleteDirectory(string path) { var directory = new DirectoryInfo(path) { Attributes = FileAttributes.Normal }; foreach (var info in directory.GetFileSystemInfos("*", SearchOption.AllDirectories)) { info.Attributes = FileAttributes.Normal; } directory.Delete(true); }
попробуйте это,
private void DeleteRecursiveFolder(string pFolderPath) { foreach (string Folder in Directory.GetDirectories(pFolderPath)) { DeleteRecursiveFolder(Folder); } foreach (string file in Directory.GetFiles(pFolderPath)) { var pPath = Path.Combine(pFolderPath, file); FileInfo fi = new FileInfo(pPath); File.SetAttributes(pPath, FileAttributes.Normal); File.Delete(file); } Directory.Delete(pFolderPath); }
другой метод без необходимости рекурсии.
public static void ForceDeleteDirectory(string path) { DirectoryInfo root; Stack<DirectoryInfo> fols; DirectoryInfo fol; fols = new Stack<DirectoryInfo>(); root = new DirectoryInfo(path); fols.Push(root); while (fols.Count > 0) { fol = fols.Pop(); fol.Attributes = fol.Attributes & ~(FileAttributes.Archive | FileAttributes.ReadOnly | FileAttributes.Hidden); foreach (DirectoryInfo d in fol.GetDirectories()) { fols.Push(d); } foreach (FileInfo f in fol.GetFiles()) { f.Attributes = f.Attributes & ~(FileAttributes.Archive | FileAttributes.ReadOnly | FileAttributes.Hidden); f.Delete(); } } root.Delete(true); }
private void DeleteRecursiveFolder(DirectoryInfo dirInfo) { foreach (var subDir in dirInfo.GetDirectories()) { DeleteRecursiveFolder(subDir); } foreach (var file in dirInfo.GetFiles()) { file.Attributes=FileAttributes.Normal; file.Delete(); } dirInfo.Delete(); }
лучшее решение-пометить все файлы как нечитаемые, а затем удалить каталог.
// delete/clear hidden attribute File.SetAttributes(filePath, File.GetAttributes(filePath) & ~FileAttributes.Hidden); // delete/clear archive and read only attributes File.SetAttributes(filePath, File.GetAttributes(filePath) & ~(FileAttributes.Archive | FileAttributes.ReadOnly));
обратите внимание, что ~ - побитовый логический оператор, который возвращает дополнение заданного двоичного значения. Я не проверял это, но он должен работать.
спасибо!
Я бы сказал, что ваш первый подход выглядит более ясным и читаемым. Второй метод пахнет отражением, не является безопасным для типа и выглядит странно. Элемент
ManagementObject
могут представлять несколько вещей, так что это не очевидно, что.InvokeMethod("Delete")
фактически удаляет каталог.
то, что мне не нравится в первом подходе (каталог.delete)-это случай, когда есть подкаталоги, которые также содержат файлы только для чтения, и у них есть подкаталоги, которые также имеют файлы только для чтения и т. д. Похоже, вам придется отключить этот флаг для каждого файла в директории и всех подкаталогов рекурсивно.
при втором подходе вы можете просто удалить этот первый каталог, и он не проверяет, доступны ли файлы только для чтения. Тем не менее, это первый раз, когда я использовал WMI в C#, так что я не совсем комфортно с ним. Поэтому я не уверен, когда следует использовать подход WMI для других приложений, а не просто использовать System.IO методы.
на первый взгляд, использование подхода WMI кажется более эффективным, чем итерация по всей файловой системе (предположим, например, что каталог имеет 10 тысяч файлов). Но я не знаю, что WMI также не делает итераций. Если это так, будучи ближе к металлу (опять же, предположения), он должен быть более эффективным.
для элегантности я признаю, что рекурсивный метод классный.
тестирование производительности должно ответить на вопрос об эффективности. И то и другое может быть элегантным если завернутый в метод расширения DirectoryInfo.
вот еще одно решение, которое позволяет избежать рекурсии на себя.
public static void DirectoryDeleteAll(string directoryPath) { var rootInfo = new DirectoryInfo(directoryPath) { Attributes = FileAttributes.Normal }; foreach (var fileInfo in rootInfo.GetFileSystemInfos()) fileInfo.Attributes = FileAttributes.Normal; foreach (var subDirectory in Directory.GetDirectories(directoryPath, "*", SearchOption.AllDirectories)) { var subInfo = new DirectoryInfo(subDirectory) { Attributes = FileAttributes.Normal }; foreach (var fileInfo in subInfo.GetFileSystemInfos()) fileInfo.Attributes = FileAttributes.Normal; } Directory.Delete(directoryPath, true); }
это работает путем сброса атрибутов в папках и файлах перед удалением, поэтому вы можете просто удалить последнюю строку для метода "DirectoryResetAttributes" и использовать delete отдельно.
на связанной заметке, в то время как это работало, у меня тогда были проблемы с удалением путей, которые были "слишком длинными" и в конечном итоге использовали решение robocopy, опубликованное здесь: C# удаление папки, которая имеет длинный пути
чтобы продолжить решение Виталия Улантикова, я дополнил его методом переименования / перемещения папки:
public static void renameFolder(String sourcePath, String targetPath) { try { if (System.IO.Directory.Exists(targetPath)) DeleteFileSystemInfo(new DirectoryInfo(targetPath)); System.IO.Directory.Move(sourcePath, targetPath); } catch (Exception ex) { Console.WriteLine("renameFolder: " + sourcePath + " " + targetPath + " " + ex.Message); throw ex; } } private static void DeleteFileSystemInfo(FileSystemInfo fsi) { fsi.Attributes = FileAttributes.Normal; var di = fsi as DirectoryInfo; if (di != null) { foreach (var dirInfo in di.GetFileSystemInfos()) { DeleteFileSystemInfo(dirInfo); } } fsi.Delete(); }