Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fast delete many files

Tags:

c#

directory

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 seconds.

When I delete files using code – 1500-4000 seconds.

Delete large number of files – don't work for me.

My code:

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

How to delete files faster?

like image 681
Alexander Brattsev Avatar asked Jun 22 '17 12:06

Alexander Brattsev


People also ask

How do I select multiple files to delete at once?

Click on one of the files or folders you want to select. Hold down the control key (Ctrl). Click on files or folders that you want to select while holding the control key. Continue to Hold down the control key until you select all the files you want.


3 Answers

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.

like image 54
Camilo Terevinto Avatar answered Oct 26 '22 09:10

Camilo Terevinto


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.

like image 1
grek40 Avatar answered Oct 26 '22 11:10

grek40


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);

        } 
    } 
}
like image 1
Tuan Le PN Avatar answered Oct 26 '22 11:10

Tuan Le PN