删除多个文件使用C#的速度有多快

A在Windows Server中有一个文件夹,包含子文件夹和~50000个文件。 当我单击鼠标右键并选择删除(或移位+删除)时 – 所有文件都会在10-20秒内被删除当我使用代码删除文件时 – 1500-4000秒!
删除大量文件 – 对我不起作用。 我的代码:

string folderPath = @"C://myFolder"; DirectoryInfo folderInfo = new DirectoryInfo(folderPath); folderInfo.Delete(true); // true - recursive, with sub-folders 

如何更快地删除文件?

删除文件的一种更快的方法是使用Windows函数而不是.NET函数。

您需要先导入该function:

 [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool DeleteFile(string lpFileName); 

然后你可以这样做:

 string[] files = Directory.EnumerateFiles(path, "*". SearchOption.AllDirectories); foreach (string file in files) { DeleteFile(file); } 

删除文件后,这是使用托管API最慢的部分,您可以调用Directory.DeleteFolder(path, true)来删除空文件夹。

由于问题实际上是关于删除网络共享文件夹,并且声明基于资源管理器的删除比C#内部删除机制快得多,因此调用基于Windows shell的删除可能会有所帮助。

 ProcessStartInfo Info = new ProcessStartInfo(); Info.Arguments = "/C rd /s /q \"\""; Info.WindowStyle = ProcessWindowStyle.Hidden; Info.CreateNoWindow = true; Info.FileName = "cmd.exe"; Process.Start(Info); 

当然,你必须替换

但是,我现在没有可用于测试性能的基础结构和文件。

不太清楚为什么删除具有大量文件和子文件夹的文件夹时DirectoryInfo.Delete()方法占用太多时间。 我怀疑这个方法也可以做很多不必要的事情。

我写了一个小类来使用Win API,而没有做太多不必要的事情来测试我的想法。 删除具有50,000个文件子文件夹的 文件夹大约需要40秒。 所以,希望它有所帮助。

我使用此PowerScript生成测试文件。

 $folder = "d:\test1"; For ($i=0; $i -lt 50000; $i++) { New-Item -Path $folder -Name "test$i.txt" -ItemType "file" -Value $i.ToString(); } 

以下是C#中的代码。

 using System; using System.Collections.Generic; // using System.Runtime.InteropServices; using System.IO; // namespace TestFileDelete { class FileDelete { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct WIN32_FIND_DATAW { public FileAttributes dwFileAttributes; public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime; public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime; public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime; public UInt32 nFileSizeHigh; // DWORD public UInt32 nFileSizeLow; // DWORD public UInt32 dwReserved0; // DWORD public UInt32 dwReserved1; // DWORD [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public String cFileName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public String cAlternateFileName; }; static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern IntPtr FindFirstFileW(String lpFileName, out WIN32_FIND_DATAW lpFindFileData); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern Boolean FindNextFileW(IntPtr hFindFile, out WIN32_FIND_DATAW lpFindFileData); [DllImport("kernel32.dll")] private static extern Boolean FindClose(IntPtr handle); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] public static extern Boolean DeleteFileW(String lpFileName); // Deletes an existing file [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern Boolean RemoveDirectoryW(String lpPathName); // Deletes an existing empty directory // This method check to see if the given folder is empty or not. public static Boolean IsEmptyFolder(String folder) { Boolean res = true; if (folder == null && folder.Length == 0) { throw new Exception(folder + "is invalid"); } WIN32_FIND_DATAW findFileData; String searchFiles = folder + @"\*.*"; IntPtr searchHandle = FindFirstFileW(searchFiles, out findFileData); if (searchHandle == INVALID_HANDLE_VALUE) { throw new Exception("Cannot check folder " + folder); } do { if ((findFileData.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory) { // found a sub folder if (findFileData.cFileName != "." && findFileData.cFileName != "..") { res = false; break; } } // if ((findFileData.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory) else { // found a file res = false; break; } } while (FindNextFileW(searchHandle, out findFileData)); FindClose(searchHandle); return res; } // public static Boolean IsEmptyFolder(String folder) // This method deletes the given folder public static Boolean DeleteFolder(String folder) { Boolean res = true; // keep non-empty folders to delete later (after we delete everything inside) Stack nonEmptyFolder = new Stack(); String currentFolder = folder; do { Boolean isEmpty = false; try { isEmpty = IsEmptyFolder(currentFolder); } catch (Exception ex) { // Something wrong res = false; break; } if (!isEmpty) { nonEmptyFolder.Push(currentFolder); WIN32_FIND_DATAW findFileData; IntPtr searchHandle = FindFirstFileW(currentFolder + @"\*.*", out findFileData); if (searchHandle != INVALID_HANDLE_VALUE) { do { // for each folder, find all of its sub folders and files String foundPath = currentFolder + @"\" + findFileData.cFileName; if ((findFileData.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory) { // found a sub folder if (findFileData.cFileName != "." && findFileData.cFileName != "..") { if (IsEmptyFolder(foundPath)) { // found an empty folder, delete it if (!(res = RemoveDirectoryW(foundPath))) { Int32 error = Marshal.GetLastWin32Error(); break; } } else { // found a non-empty folder nonEmptyFolder.Push(foundPath); } } // if (findFileData.cFileName != "." && findFileData.cFileName != "..") } // if ((findFileData.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory) else { // found a file, delete it if (!(res = DeleteFileW(foundPath))) { Int32 error = Marshal.GetLastWin32Error(); break; } } } while (FindNextFileW(searchHandle, out findFileData)); FindClose(searchHandle); } // if (searchHandle != INVALID_HANDLE_VALUE) }// if (!IsEmptyFolder(folder)) else { if (!(res = RemoveDirectoryW(currentFolder))) { Int32 error = Marshal.GetLastWin32Error(); break; } } if (nonEmptyFolder.Count > 0) { currentFolder = nonEmptyFolder.Pop(); } else { currentFolder = null; } } while (currentFolder != null && res); return res; } // public static Boolean DeleteFolder(String folder) }; class Program { static void Main(string[] args) { DateTime t1 = DateTime.Now; try { Boolean b = FileDelete.DeleteFolder(@"d:\test1"); } catch (Exception ex) { Console.WriteLine(ex.Message); } DateTime t2 = DateTime.Now; TimeSpan ts = t2 - t1; Console.WriteLine(ts.Seconds); } } }