How to fast delete many files

前端 未结 3 1411
梦如初夏
梦如初夏 2021-01-21 19:09

I have a folder in Windows Server with subfolders and ≈50000 files. When I click the right mouse button and choose delete (or shift+delete) – all files are deleted in 10-20 seco

相关标签:
3条回答
  • 2021-01-21 19:54

    Not quite sure why the method DirectoryInfo.Delete() takes too much time when deleting folders that have a lot of files and sub-folders. I suspect that the method may also do quite a few things that are unnecessary.

    I write a small class to to use Win API without doing too many unnecessary things to test my idea. It takes about 40 seconds to delete a folder that have 50,000 files and sub-folders. So, hope it helps.

    I use this PowerScript to generate the testing files.

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

    The following is the code in 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<String> nonEmptyFolder = new Stack<String>();
                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);
    
            } 
        } 
    }
    
    0 讨论(0)
  • 2021-01-21 20:02

    A much faster way to delete files is to use the Windows functions instead of the .NET ones.

    You will need to first import the function:

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

    And then you can do this:

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

    Once the files are deleted, which is the slowest part by using the managed APIs, you can call Directory.DeleteFolder(path, true) to delete the empty folders.

    0 讨论(0)
  • Since the question is actually about deleting network shared folders and it's stated that the explorer based delete is much faster than the C# internal delete mechanism, it might help to just invoke a windows shell based delete.

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

    Ofcourse, you have to replace <your-path>.

    However, I don't have the infrastructure and files available to test the performance myself right now.

    0 讨论(0)
提交回复
热议问题