Как удалить каталог с файлами только для чтения в 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
to Normal
рекурсивно, затем удаляет элементы:
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));
обратите внимание, что ~ - побитовый логический оператор, который возвращает дополнение заданного двоичного значения. Я не проверял, но это должно сработать.
спасибо!
Я бы сказал, что ваш первый подход выглядит более ясным и читаемым. Второй метод пахнет отражением, не является безопасным и выглядит странно. The ManagementObject
может представлять несколько вещей, поэтому не очевидно, что .InvokeMethod("Delete")
фактически удаляет каталог.
то, что мне не нравится в первом подходе (каталог.delete) - это случай, когда есть подкаталоги, которые также содержат файлы только для чтения, и у них есть подкаталоги, которые также имеют файлы только для чтения, и так далее. Похоже, вам придется отключить этот флаг для каждого файла в каталоге и всех подкаталогах рекурсивно.
при втором подходе вы можете просто удалить этот первый каталог, и он не проверяет, доступны ли файлы только для чтения. Тем не менее, это первый раз, когда я использовал WMI в C#, поэтому мне это не очень удобно. Поэтому я не уверен, когда идти с подходом WMI для других приложений, а не просто использовать систему.Методы ввода-вывода.
на поверхности использование подхода 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# удаление папки, которая имеет длинный пути
для последующего решения Виталий Ulantikov я дополнил его с переименовать/переместить папку способ:
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();
}