program tip

특정 경로의 모든 파일과 디렉토리를 빠르게 가져옵니다.

radiobox 2020. 11. 12. 08:04
반응형

특정 경로의 모든 파일과 디렉토리를 빠르게 가져옵니다.


C #이 디렉터리를 스캔하는 백업 응용 프로그램을 만들고 있습니다. 디렉토리에있는 모든 파일과 서브 파일을 가져 오기 위해 다음과 같은 것을 사용하기 전에 :

DirectoryInfo di = new DirectoryInfo("A:\\");
var directories= di.GetFiles("*", SearchOption.AllDirectories);

foreach (FileInfo d in directories)
{
       //Add files to a list so that later they can be compared to see if each file
       // needs to be copid or not
}

유일한 문제는 때때로 파일에 액세스 할 수없고 몇 가지 오류가 발생한다는 것입니다. 내가 얻는 오류의 예는 다음과 같습니다.오류

결과적으로 현재 디렉토리의 모든 파일을 스캔하는 재귀 적 방법을 만들었습니다. 해당 디렉토리에 디렉토리가 있으면 해당 디렉토리를 전달하여 메서드가 다시 호출됩니다. 이 방법의 좋은 점은 오류가없는 경우 해당 파일을 목록에 추가하고 오류가있는 경우 다른 목록에 디렉토리를 추가하는 옵션을 제공하는 try catch 블록 안에 파일을 배치 할 수 있다는 것입니다.

try
{
    files = di.GetFiles(searchPattern, SearchOption.TopDirectoryOnly);               
}
catch
{
     //info of this folder was not able to get
     lstFilesErrors.Add(sDir(di));
     return;
}

따라서이 방법은 훌륭하게 작동합니다. 유일한 문제는 큰 디렉토리를 스캔 할 때 많은 시간이 걸린다는 것입니다. 이 프로세스의 속도를 어떻게 높일 수 있습니까? 내 실제 방법은 필요한 경우입니다.

private void startScan(DirectoryInfo di)
{
    //lstFilesErrors is a list of MyFile objects
    // I created that class because I wanted to store more specific information
    // about a file such as its comparePath name and other properties that I need 
    // in order to compare it with another list

    // lstFiles is a list of MyFile objects that store all the files
    // that are contained in path that I want to scan

    FileInfo[] files = null;
    DirectoryInfo[] directories = null;
    string searchPattern = "*.*";

    try
    {
        files = di.GetFiles(searchPattern, SearchOption.TopDirectoryOnly);               
    }
    catch
    {
        //info of this folder was not able to get
        lstFilesErrors.Add(sDir(di));
        return;
    }

    // if there are files in the directory then add those files to the list
    if (files != null)
    {
        foreach (FileInfo f in files)
        {
            lstFiles.Add(sFile(f));
        }
    }


    try
    {
        directories = di.GetDirectories(searchPattern, SearchOption.TopDirectoryOnly);
    }
    catch
    {
        lstFilesErrors.Add(sDir(di));
        return;
    }

    // if that directory has more directories then add them to the list then 
    // execute this function
    if (directories != null)
        foreach (DirectoryInfo d in directories)
        {
            FileInfo[] subFiles = null;
            DirectoryInfo[] subDir = null;

            bool isThereAnError = false;

            try
            {
                subFiles = d.GetFiles();
                subDir = d.GetDirectories();

            }
            catch
            {
                isThereAnError = true;                                                
            }

            if (isThereAnError)
                lstFilesErrors.Add(sDir(d));
            else
            {
                lstFiles.Add(sDir(d));
                startScan(d);
            }


        }

}

다음과 같이 예외를 처리하려고하면 문제가 해결됩니다.

DirectoryInfo di = new DirectoryInfo("A:\\");
FileInfo[] directories = null;
            try
            {
                directories = di.GetFiles("*", SearchOption.AllDirectories);

            }
            catch (UnauthorizedAccessException e)
            {
                Console.WriteLine("There was an error with UnauthorizedAccessException");
            }
            catch
            {
                Console.WriteLine("There was antother error");
            }

예외가 발생하면 파일을 얻지 못합니다.


이 방법은 훨씬 빠릅니다. 디렉토리에 많은 파일을 배치 할 때만 전화를 걸 수 있습니다. 내 A : \ 외장 하드 드라이브에는 거의 1 테라 비트가 포함되어있어 많은 파일을 처리 할 때 큰 차이가 있습니다.

static void Main(string[] args)
{
    DirectoryInfo di = new DirectoryInfo("A:\\");
    FullDirList(di, "*");
    Console.WriteLine("Done");
    Console.Read();
}

static List<FileInfo> files = new List<FileInfo>();  // List that will hold the files and subfiles in path
static List<DirectoryInfo> folders = new List<DirectoryInfo>(); // List that hold direcotries that cannot be accessed
static void FullDirList(DirectoryInfo dir, string searchPattern)
{
    // Console.WriteLine("Directory {0}", dir.FullName);
    // list the files
    try
    {
        foreach (FileInfo f in dir.GetFiles(searchPattern))
        {
            //Console.WriteLine("File {0}", f.FullName);
            files.Add(f);                    
        }
    }
    catch
    {
        Console.WriteLine("Directory {0}  \n could not be accessed!!!!", dir.FullName);                
        return;  // We alredy got an error trying to access dir so dont try to access it again
    }

    // process each directory
    // If I have been able to see the files in the directory I should also be able 
    // to look at its directories so I dont think I should place this in a try catch block
    foreach (DirectoryInfo d in dir.GetDirectories())
    {
        folders.Add(d);
        FullDirList(d, searchPattern);                    
    }

}

그건 그렇고 나는 당신의 의견 덕분에 이것을 얻었습니다 Jim Mischel


.NET 4.0에는를 반환하고 메모리의 모든 파일을로드하지 않는 Directory.EnumerateFiles 메서드가 IEnumerable<string>있습니다. 반환 된 컬렉션에 대해 반복을 시작하면 파일이 반환되고 예외가 처리 될 수 있습니다 .


.NET 파일 열거 방법이 느리다는 오랜 역사가 있습니다. 문제는 큰 디렉토리 구조를 열거하는 즉각적인 방법이 없다는 것입니다. 여기에서 받아 들여지는 대답조차도 GC 할당에 문제가 있습니다.

내가 할 수있는 최선은 내 라이브러리에 래핑되어 CSharpTest.Net.IO 네임 스페이스 FindFile ( 소스 ) 클래스 로 노출되는 것 입니다. 이 클래스는 불필요한 GC 할당 및 문자열 마샬링없이 파일과 폴더를 열거 할 수 있습니다.

사용법은 충분히 간단하며 RaiseOnAccessDenied 속성은 사용자가 액세스 할 수없는 디렉터리와 파일을 건너 뜁니다.

    private static long SizeOf(string directory)
    {
        var fcounter = new CSharpTest.Net.IO.FindFile(directory, "*", true, true, true);
        fcounter.RaiseOnAccessDenied = false;

        long size = 0, total = 0;
        fcounter.FileFound +=
            (o, e) =>
            {
                if (!e.IsDirectory)
                {
                    Interlocked.Increment(ref total);
                    size += e.Length;
                }
            };

        Stopwatch sw = Stopwatch.StartNew();
        fcounter.Find();
        Console.WriteLine("Enumerated {0:n0} files totaling {1:n0} bytes in {2:n3} seconds.",
                          total, size, sw.Elapsed.TotalSeconds);
        return size;
    }

내 로컬 C : \ 드라이브의 경우 다음을 출력합니다.

232.876 초에 총 307,707,792,662 바이트의 파일 810,046 개를 열거했습니다.

마일리지는 드라이브 속도에 따라 다를 수 있지만 이것은 관리 코드에서 파일을 열거하는 가장 빠른 방법입니다. 이벤트 매개 변수는 FindFile.FileFoundEventArgs 유형의 변경 클래스 이므로 발생하는 각 이벤트에 대해 값이 변경되므로 참조를 유지하지 마십시오.


이것을 사용하여 모든 디렉토리와 하위 디렉토리를 가져올 수 있습니다. 그런 다음 간단히 반복하여 파일을 처리합니다.

string[] folders = System.IO.Directory.GetDirectories(@"C:\My Sample Path\","*", System.IO.SearchOption.AllDirectories);

foreach(string f in folders)
{
   //call some function to get all files in folder
}

나는 이것이 오래되었다는 것을 알고 있지만 ... 또 다른 옵션은 FileSystemWatcher를 다음과 같이 사용하는 것입니다.

void SomeMethod()
{
    System.IO.FileSystemWatcher m_Watcher = new System.IO.FileSystemWatcher();
    m_Watcher.Path = path;
    m_Watcher.Filter = "*.*";
    m_Watcher.NotifyFilter = m_Watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
    m_Watcher.Created += new FileSystemEventHandler(OnChanged);
    m_Watcher.EnableRaisingEvents = true;
}

private void OnChanged(object sender, FileSystemEventArgs e)
    {
        string path = e.FullPath;

        lock (listLock)
        {
            pathsToUpload.Add(path);
        }
    }

이렇게하면 매우 간단한 프로세스로 디렉토리에서 파일 변경 사항을 감시 할 수 있으며, 변경된 파일 이름을 저장하여 적절한 시간에 백업 할 수 있습니다.


(다른 질문의 다른 답변 에서이 조각을 복사했습니다)

Show progress when searching all files in a directory

Fast files enumeration

Of course, as you already know, there are a lot of ways of doing the enumeration itself... but none will be instantaneous. You could try using the USN Journal of the file system to do the scan. Take a look at this project in CodePlex: MFT Scanner in VB.NET... it found all the files in my IDE SATA (not SSD) drive in less than 15 seconds, and found 311000 files.

You will have to filter the files by path, so that only the files inside the path you are looking are returned. But that is the easy part of the job!


Maybe it will be helpfull for you. You could use "DirectoryInfo.EnumerateFiles" method and handle UnauthorizedAccessException as you need.

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        DirectoryInfo diTop = new DirectoryInfo(@"d:\");
        try
        {
            foreach (var fi in diTop.EnumerateFiles())
            {
                try
                {
                    // Display each file over 10 MB; 
                    if (fi.Length > 10000000)
                    {
                        Console.WriteLine("{0}\t\t{1}", fi.FullName, fi.Length.ToString("N0"));
                    }
                }
                catch (UnauthorizedAccessException UnAuthTop)
                {
                    Console.WriteLine("{0}", UnAuthTop.Message);
                }
            }

            foreach (var di in diTop.EnumerateDirectories("*"))
            {
                try
                {
                    foreach (var fi in di.EnumerateFiles("*", SearchOption.AllDirectories))
                    {
                        try
                        {
                            // Display each file over 10 MB; 
                            if (fi.Length > 10000000)
                            {
                                Console.WriteLine("{0}\t\t{1}",  fi.FullName, fi.Length.ToString("N0"));
                            }
                        }
                        catch (UnauthorizedAccessException UnAuthFile)
                        {
                            Console.WriteLine("UnAuthFile: {0}", UnAuthFile.Message);
                        }
                    }
                }
                catch (UnauthorizedAccessException UnAuthSubDir)
                {
                    Console.WriteLine("UnAuthSubDir: {0}", UnAuthSubDir.Message);
                }
            }
        }
        catch (DirectoryNotFoundException DirNotFound)
        {
            Console.WriteLine("{0}", DirNotFound.Message);
        }
        catch (UnauthorizedAccessException UnAuthDir)
        {
            Console.WriteLine("UnAuthDir: {0}", UnAuthDir.Message);
        }
        catch (PathTooLongException LongPath)
        {
            Console.WriteLine("{0}", LongPath.Message);
        }
    }
}

참고 URL : https://stackoverflow.com/questions/6061957/get-all-files-and-directories-in-specific-path-fast

반응형