如何删除C#中只读文件的目录?
我需要删除一个包含只读文件的目录。 哪种方法更好:
-
使用
DirectoryInfo.Delete()
,或者, -
ManagementObject.InvokeMethod("Delete")
?
与DirectoryInfo.Delete()
,我必须手动closures每个文件的只读属性,但ManagementObject.InvokeMethod("Delete")
似乎不需要。 有没有哪种情况比另一种更可取?
示例代码(test.txt是只读的)。
第一种方法:
DirectoryInfo dir = new DirectoryInfo(@"C:\Users\David\Desktop\"); dir.CreateSubdirectory("Test"); DirectoryInfo test = new DirectoryInfo(@"C:\Users\David\Desktop\Test\"); File.Copy(@"C:\Users\David\Desktop\test.txt", @"C:\Users\David\Desktop\Test\test.txt"); File.SetAttributes(@"C:\Users\David\Desktop\Test\test.txt", FileAttributes.Archive); test.Delete(true);
第二种方式:
DirectoryInfo dir = new DirectoryInfo(@"C:\Users\David\Desktop\"); dir.CreateSubdirectory("Test"); DirectoryInfo test = new DirectoryInfo(@"C:\Users\David\Desktop\Test\"); File.Copy(@"C:\Users\David\Desktop\test.txt", @"C:\Users\David\Desktop\Test\test.txt"); string folder = @"C:\Users\David\Desktop\Test"; 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) { } }
这是一个扩展方法,它将Attributes
recursion设置为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(); }
避免recursion调用的最简单方法是在获取FileSystemInfo
时使用AllDirectories
选项,如下所示:
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); }
另一种方法不需要recursion。
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));
请注意,〜是一个按位逻辑运算符,它返回给定二进制值的补码。 我没有testing过这个,但是应该可以。
谢谢!
我会说,你的第一个方法看起来更加明确和可读性。 第二种方法闻起来像reflection,不安全,看起来很奇怪。 ManagementObject
可以表示多个东西,所以不明显.InvokeMethod("Delete")
实际上删除了一个目录。
我不喜欢关于第一种方法(directory.delete)的事情是有子目录也包含只读文件,并且它们也有只读文件的子目录等等。 好像你必须closures该目录中的每个文件和所有子目录recursion的标志。
通过第二种方法,您可以删除第一个目录,而不检查文件是否是只读的。 不过,这是我第一次在C#中使用WMI,所以我并不是那么满意。 所以我不确定何时使用其他应用程序的WMI方法,而不是使用System.IO方法。
从表面上看,使用WMI方法似乎比遍历整个文件系统更有效率(假设目录有数千个文件)。 但是我不知道WMI也不会做迭代。 如果是这样,接近金属(再次假设)应该更有效率。
为了优雅,我承认recursion方法很酷。
性能testing应该回答效率问题。 如果用DirectoryInfo的扩展方法包装,可以是优雅的。
这是避免自身recursion的另一个解决scheme。
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。
在一个相关的说明,虽然这个工作,然后我有问题,删除“太长”的path,并最终使用robocopy解决scheme张贴在这里: C#删除一个文件夹,有长path
为了跟上Vitaliy Ulantikov的解决scheme,我补充了一个重命名/移动文件夹方法:
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(); }