2014-01-08 40 views
3
var myFileList = new List<MyCustomFileInfo>(); 
var dir = new DirectoryInfo(@"C:\SomeDir"); 

foreach (var file in dir.GetFiles()) 
{ 
    myFileList.Add(new MyCustomFileInfo() 
    { 
     Filename = file.Name, 
     ModifiedOn = file.LastWriteTime, 
     SizeInBytes = (int)file.Length 
    }); 
} 

dir.GetFiles執行得非常快,但是當我開始訪問屬性時,似乎個別調用了文件系統(這很慢)。什麼是使用C#獲取包含文件大小的文件的元數據的快速方法?

我該如何重寫這個,這樣我才能以更有效的方式獲得所有文件名,lastWriteTime和文件大小?

Nb。代碼被削減以說明這一點。我現實世界的用例更復雜(同步),但這是性能問題的核心。

+0

哪個文件系統?你需要支持FAT還是隻有NTFS才行? –

+0

我不確定是否/如何改進(不會懷疑),但獲取除名稱以外的目錄內容的任何信息都將成爲性能打擊,無論如何。 – Jon

+0

不完全確定,@ThomasW。在我的具體情況下,它是一個網絡共享,但您可以通過需要NTFS的通用方式回答 - 如果它不適合我,它可能會幫助其他人。 :) – Kjensen

回答

2

我不知道是否使用dir.GetFileSystemInfos()會更快。

編輯:

我通過與dotPeek,相關的代碼看起來那麼也許不是見下文!

無論哪種方式,如果你在使用Windows部署,您應該能夠使用FindFirstFile家庭的Win32本地功能,這是在蓋什麼.Length等做(但正如你正確地假設,他們對文件的做FindFirstFile完整路徑,閱讀等)

編輯2:

我通過代碼再次一看,它看起來像GetFileSystemInfos應該填充FileInfos和DirectoryInfos從底層系統調用數據。 (您應該可以通過查看* Info上的_dataInitialised專用字段來驗證這一點 - 如果它爲零,那麼它被初始化,如果它是-1,那麼它不是)。

+0

GetFiles實際上*不會*使用FindFirstFile在 –

+0

下我已經嘗試了GetFilsystemInfos,但FileSystemInfo不包含長度(或類似)屬性,不幸的是。 ( – Kjensen

+0

@Kjensen:你可以把FileSystemInfo轉換成FileInfo,FI是FSI的子類 – AKX

-1

無論如何,您應該從循環繼續測試條件中刪除函數調用。

var myFileList = new List<MyCustomFileInfo>(); 
var dir = new DirectoryInfo(@"C:\SomeDir"); 
var files = dir.GetFiles(); // called 1 time only 

foreach (var file in files) 
{ 
    myFileList.Add(new MyCustomFileInfo() 
    { 
     Filename = file.Name, 
     ModifiedOn = file.LastWriteTime, 
     SizeInBytes = (int)file.Length 
    }); 
} 
+1

GetFiles返回一個文件名的數組,這是一個IEnumerable,因此foreach將枚舉它,GetFiles將只被調用一次 – CMircea

+0

@CMircea但是如果你有n個文件,GetFiles()函數被調用n次,IEnumerable被創建n次,不是嗎?在我的解決方案中,它被稱爲1次 –

+0

@CMircea如果文件被添加到'dir' while循環運行?我的版本不會循環這個新的文件,而Kjensen版本的問題會。我誤解了嗎?你可以給你的評論的鏈接嗎? –

0

這是絕對正常的方法。如果速度慢,那麼......購買SSD或製作RAID(SSD RAID最好是^^)。

如果問題引起的「緩慢」,是不負責任的UI,然後做一個非常簡單的方式:填充只有名稱的文件的列表(這是快),然後運行一個線程,這將獲取附加數據列表中的每個項目。甚至可以使用虛擬列表來只讀取顯示的當前數據。

其他的事情可能是緩存最近的結果,因此,如果你回到以前的目錄,結果會立即可用,無需重新加載一切。

0

遍歷所有文件的文件夾在本質上是一個緩慢的操作,不管底層的存儲是什麼。

在某些情況下,您可以使用EnumerateFiles而不是GetFiles來提高性能,並返回IEnumerable而不是數組或列表。這樣,如果使用Take(),Skip(),First()和枚舉所有內容之前可以返回的其他函數,則可以避免遍歷所有文件。您也可以使用Enumerable.Select將IEnumerable轉換爲您自己的類,儘管這會導致對每個處理文件進行系統調用。

不幸的是,如果要將當前文件系統狀態與先前的快照進行比較,則在同步情況下(上下文非常重要),這不起作用。在這種情況下,遠的最好使用FileSystemWatcher等待在處理之前想要同步的文件夾的實際更改。

一旦您檢測到文件夾中的更改,您可以稍等片刻讓他們在處理整個文件夾之前停止,或者記錄所有更改事件並僅處理實際更改的文件。如果有很多操作正在進行(例如,如果您複製了數千個文件的存儲庫),則FileSystemWatcher可能會錯過事件,但通常會通過收到文件以某種方式發生更改的通知。

如果您確定您使用的是NTFS,事情會變得相當容易。在這種情況下,您可以啓用日記功能,並獲取自上次檢查後更改的所有文件的列表。您還可以使用卷影服務來讀取甚至打開的文件,使用Transactional NTFS以安全的方式修改文件等。這些功能需要本地調用,但AlphaFS項目提供了一個庫。

AlphaFS在內部使用擴展方法來搜索文件,如FindFirstFileEx。在Windows 7 +中,此功能可以使用大緩衝區來提高性能。

另一個好處是日記或NTFS的對象ID允許您檢測重命名或文件移動(基本上是同一件事)並避免不必要的文件同步。

相關問題