static void Main(string[] args) { try { // DeleteFilesWithProgress.exe d:\home\logfiles\SalesForceIntegration if (args.Length == 0) { Console.WriteLine(@"Usage: DeleteFilesWithProgress.exe d:\home\logfiles\SalesForceIntegration"); return; } // delete file older than 10 mins FileExtensions.DeleteIfOlderTime = DateTime.UtcNow.AddMinutes(-10); var path = new DirectoryInfo(args[0]); int folderCount; int filesCount; string largestFile; long largestFileSizeInBytes; string largestFolder; int largestFolderChildrenCount; int deletedFileCount = 1; bool incompleteScan; var size = FileExtensions.GetDirectorySizeInBytes( path: path.FullName, skipSymLink: true, continueOnError: false, cancellationToken: CancellationToken.None, maxItemCount: 10000, pauseInterval: 100, folderCount: out folderCount, filesCount: out filesCount, largestFile: out largestFile, largestFileSizeInBytes: out largestFileSizeInBytes, largestFolder: out largestFolder, largestFolderChildrenCount: out largestFolderChildrenCount, deletedFileCount: out deletedFileCount, incompleteScan: out incompleteScan); Console.WriteLine("size: {0:n0} bytes", size); Console.WriteLine("folderCount: {0:n0} folders", folderCount); Console.WriteLine("filesCount: {0:n0} files", filesCount); Console.WriteLine("largestFile: {0}", largestFile); Console.WriteLine("largestFileSize: {0:n0} bytes", largestFileSizeInBytes); Console.WriteLine("largestFolder: {0}", largestFolder); Console.WriteLine("largestFolderChildrenCount: {0:n0} items", largestFolderChildrenCount); Console.WriteLine("deletedFileCount: {0:n0} files", deletedFileCount); Console.WriteLine("incompleteScan: {0}", incompleteScan); } catch (Exception ex) { Console.WriteLine(ex); } }
//public static long GetDirectorySizeInBytes(string path, out int folderCount, out int filesCount, bool continueOnError) //{ // return GetDirectorySizeInBytes(path, skipSymLink: false, continueOnError: continueOnError, folderCount: out folderCount, filesCount: out filesCount); //} //public static long GetDirectorySizeInBytes(string path, bool skipSymLink, bool continueOnError, out int folderCount, out int filesCount) //{ // return GetDirectorySizeInBytes( // path, skipSymLink, continueOnError, CancellationToken.None, // maxItemCount: -1, pauseInterval: 0, // folderCount: out folderCount, filesCount: out filesCount, // largestFile: out _, largestFileSizeInBytes: out _, largestFolder: out _, largestFolderChildrenCount: out _, // incompleteScan: out _); //} /// <summary> /// This method is optimized to avoid recursion and we use pinvoke to retrieve the file attributes in the same call since /// performance is critical for this routine. /// </summary> public static long GetDirectorySizeInBytes( string path, bool skipSymLink, bool continueOnError, CancellationToken cancellationToken, int maxItemCount, int pauseInterval, out int folderCount, out int filesCount, out string largestFile, out long largestFileSizeInBytes, out string largestFolder, out int largestFolderChildrenCount, out int deletedFileCount, out bool incompleteScan) { Console.Write($"{DateTime.UtcNow:s}"); IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); int currentDeletedFileCount = 0; largestFile = null; largestFileSizeInBytes = 0; largestFolder = null; largestFolderChildrenCount = 0; incompleteScan = true; deletedFileCount = 0; List <string> toDeletes = new List <string>(); Queue <string> foldersQueue = new Queue <string>(); foldersQueue.Enqueue(path); folderCount = filesCount = 0; long size = 0; while (foldersQueue.Count > 0) { if (cancellationToken.IsCancellationRequested) { return(size); } string currentFolder = foldersQueue.Dequeue(); int childrenCount = 0; NativeMethods.WIN32_FIND_DATAW findData; IntPtr handle = IntPtr.Zero; try { // Uses a larger buffer for directory queries, which can increase performance of the find operation. handle = NativeMethods.FindFirstFileExW(ToLongPath(currentFolder).TrimEnd('\\') + @"\*", NativeMethods.FINDEX_INFO_LEVELS.FindExInfoBasic, // does not query the short file name, improving overall enumeration speed out findData, NativeMethods.FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, NativeMethods.FIND_FIRST_EX_LARGE_FETCH); if (handle == INVALID_HANDLE_VALUE) { int error = Marshal.GetLastWin32Error(); string message = string.Format("{0}. {1}", new Win32Exception(error).Message, currentFolder); Win32Exception exception = new Win32Exception(error, message); exception.Data.Add("Method", "FindFirstFile"); exception.Data.Add("Error", "INVALID_HANDLE_VALUE"); exception.Data.Add("Path", currentFolder); throw exception; } do { // Pause for a little while to avoid spinning 100% CPU when enumerating a large directory, if requested. if (pauseInterval > 1 && (folderCount + filesCount + 1) % pauseInterval == 0) { if ((folderCount + filesCount + 1) % (pauseInterval * 5) == 0) { Console.WriteLine(); Console.Write($"{DateTime.UtcNow:s}({deletedFileCount - currentDeletedFileCount} files deleted)"); currentDeletedFileCount = deletedFileCount; } Console.Write('.'); Thread.Sleep(100); } if (cancellationToken.IsCancellationRequested) { return(size); } if ((findData.dwFileAttributes & FileAttributes.Directory) != 0) { // this is a directory if (skipSymLink && (findData.dwFileAttributes & FileAttributes.ReparsePoint) != 0) { // We want to skip symlinks when directed to continue; } if (findData.cFileName != "." && findData.cFileName != "..") { // count the folder folderCount++; childrenCount++; // Every folder will be charged an extra 1K for file system metadata. // REMARKS: FSRM uses the same logic for calculating usage which // helps blocking tenants to store a bunch of folders since // they can still fill their quota. size += FileSystemMetadataSize; foldersQueue.Enqueue(currentFolder.TrimEnd('\\') + "\\" + findData.cFileName); // Impose a limit on the total number of items we will go through. if (maxItemCount != -1 && (folderCount + filesCount > maxItemCount)) { Console.WriteLine("Hit max limit " + maxItemCount + " items!"); return(size); } } continue; } var ftLastWriteTime = ((long)findData.ftLastWriteTime.dwHighDateTime << 32) + findData.ftLastWriteTime.dwLowDateTime; var lastWriteTime = DateTime.FromFileTimeUtc(ftLastWriteTime); var toDelete = lastWriteTime < DeleteIfOlderTime; if (toDelete) { toDeletes.Add(currentFolder.TrimEnd('\\') + "\\" + findData.cFileName); if (toDeletes.Count >= 25) { var deleted = 0; Parallel.ForEach(toDeletes, f => { if (FileExtensions.DeleteFileInternal(f, throwIfError: false)) { Interlocked.Increment(ref deleted); } }); deletedFileCount += deleted; toDeletes.Clear(); } } // count the file filesCount++; childrenCount++; // get file size and aggregate the value long filesize = (long)findData.nFileSizeHigh << 32 | (long)findData.nFileSizeLow; size += filesize; // Track file with the largest size. if (filesize > largestFileSizeInBytes) { largestFile = Path.Combine(currentFolder, findData.cFileName); largestFileSizeInBytes = filesize; } // Every file will be charged an extra 1K for file system metadata. // REMARKS: FSRM uses the same logic for calculating usage which // helps blocking tenants to store a bunch of files with no data since // they can still fill their quota. size += FileSystemMetadataSize; // Impose a limit on the total number of items we will go through. if (maxItemCount != -1 && (folderCount + filesCount > maxItemCount)) { return(size); } }while (NativeMethods.FindNextFileW(handle, out findData)); // Track folder with the most items. if (childrenCount > largestFolderChildrenCount) { largestFolder = currentFolder; largestFolderChildrenCount = childrenCount; } } catch (Exception ex) { if (continueOnError) { // log and continue Console.WriteLine(ex); } else { throw; } } finally { if (handle != IntPtr.Zero && handle != INVALID_HANDLE_VALUE) { NativeMethods.FindClose(handle); } handle = IntPtr.Zero; } } incompleteScan = false; return(size); }