将大量文件写入 Windows 上的 HDD
一个文件夹中包含的文件过多会导致文件操作减慢程序的速度。这里提供的类旨在为文件写入提供一个树状文件夹结构。
引言
如果您向一个文件夹写入大约一千个文件,然后尝试用资源管理器打开这个文件夹,您可能会注意到资源管理器开始变慢。如果您放入100,000个文件,程序中新建文件的操作将会降低所有性能。解决这个问题的方法是不要在一个文件夹中放置太多文件。首先想到的是文件夹的分层结构,每个文件夹包含不超过N个文件。这正是这里提供的类所做的事情。
分层结构
建议的层次结构如图所示(于2012年4月18日组成)
首先是根文件夹 - 您应该传递给构造函数的文件夹。在这种情况下,它是C:\FolderProviderTest。然后是名称中包含当前日期的文件夹。日期格式为 "dd_MM_yyyy
",最低级别的文件夹的名称包含当前的小时、分钟和序列号,格式为 "HH_mms
",其中 s 是连续递增的序列号。
Using the Code
想法
每次需要写入文件时,您都需要目标文件夹名称。有人应该注意以下几点
- 给定的文件夹必须存在。
- 文件夹应该只包含允许数量的文件。如果超过这个数量,应该创建另一个文件夹并给出它的名称。
- 分层结构的所有逻辑都应该被隐藏。
这就是TreeFolderProvider
类所做的。
TreeFolderProvider类
对于您需要在磁盘上写入的每个文件,只需调用TreeFolderProvider.NextFolder
方法 - 您将获得正确的写入文件夹。然后,您可以使用Path.Combine
方法来获取完整的文件名。您还可以通过设置TreeFolderProvider.
MaxAllowedInOneFolder
属性来指定一个文件夹中应该有多少文件。请记住,NextFolder
方法将精确地返回相同的文件夹名称MaxAllowedInOneFolder
次。然后,文件夹名称将更改。因此,对于每个文件,您应该精确地调用NextFolder
方法一次。
public class TreeFolderProvider:IFolderProvider
{
private string rootFolder;
private DateTime creationTime = DateTime.Now;
private int maxAllowedInOneFolder = 5;
private string currentFolder;
private int filesInCurrentFolder = 0;
long folderChangeCount = 0;
private DateTime todayFolder = DateTime.Now.Date;
private System.Timers.Timer dateChecker = new System.Timers.Timer(60000);
public static bool requireFolderWork = true;//set into false for testing purposes only
void dateChecker_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
CheckDate(DateTime.Now);
}
private static void requireFolder(string mustBe)
{
if (requireFolderWork == true)
if (Directory.Exists(mustBe) == false)
Directory.CreateDirectory(mustBe);
}
private void makeNextFolder()
{
long folderNumber = Interlocked.Increment(ref folderChangeCount);
string date = todayFolder.ToString("dd_MM_yyyy");
string time = DateTime.Now.ToString("HH_mm") + folderNumber.ToString();
string datePath = Path.Combine(rootFolder, date);
requireFolder(datePath);
string nextPath = Path.Combine(datePath, time);
requireFolder(nextPath);
currentFolder = nextPath;
filesInCurrentFolder = 1;
}
public TreeFolderProvider(string folderName)
{
rootFolder = folderName;
requireFolder(folderName);
makeNextFolder();
filesInCurrentFolder = 0;
dateChecker.Elapsed += new System.Timers.ElapsedEventHandler(dateChecker_Elapsed);
dateChecker.Start();
}
public int MaxAllowedInOneFolder
{
get { return maxAllowedInOneFolder; }
set { maxAllowedInOneFolder = value; }
}
public string RootFolder { get { return rootFolder; } }
public string CurrentFolder { get { return currentFolder; } }
public int FilesInCurrentFolder { get { return filesInCurrentFolder; } }
public long FolderChangeCount { get { return folderChangeCount; } }
public string NextFolder()
{
Interlocked.Increment(ref filesInCurrentFolder);
if (filesInCurrentFolder > maxAllowedInOneFolder)
{
makeNextFolder();
}
return currentFolder;
}
public void CheckDate(DateTime toCheck)
{
if (todayFolder.Equals(toCheck.Date) == false)
{
todayFolder = toCheck.Date;
makeNextFolder();
}
}
public void Dispose()
{
dateChecker.Dispose();
}
}
FolderChangeCount
显示了文件夹实际更改了多少次,即调用了private
makeNextFolder
方法。
一个类实现了IDisposable
,因为有一个计时器会检查下一个日期是否已经到来。在这种情况下,文件夹会通过makeNextFolder
方法进行“重新计算”。
测试应用程序
示例程序中的控制台应用程序创建了TreeFolderProvider
类的一个新实例,将MaxAllowedInOneFolder
属性设置为3
,然后每五秒钟调用NextFolder
方法,打印结果。
static void Main(string[] args)
{
TreeFolderProvider provider = new TreeFolderProvider(@"C:\FolderProviderTest");
provider.MaxAllowedInOneFolder = 3;
for (int i = 0; i < 20; ++i)
{
Console.WriteLine(provider.NextFolder());
Thread.Sleep(5000);
}
Console.WriteLine("Press any key to continue");
Console.ReadKey();
}
输出如下所示。结果文件夹结构显示在文章开头的图片中。
C:\FolderProviderTest\18_04_2012\19_591
C:\FolderProviderTest\18_04_2012\19_591
C:\FolderProviderTest\18_04_2012\19_591
C:\FolderProviderTest\18_04_2012\19_592
C:\FolderProviderTest\18_04_2012\19_592
C:\FolderProviderTest\18_04_2012\19_592
C:\FolderProviderTest\18_04_2012\19_593
C:\FolderProviderTest\18_04_2012\19_593
C:\FolderProviderTest\18_04_2012\19_593
C:\FolderProviderTest\18_04_2012\19_594
C:\FolderProviderTest\18_04_2012\19_594
C:\FolderProviderTest\18_04_2012\19_594
C:\FolderProviderTest\18_04_2012\20_005
C:\FolderProviderTest\18_04_2012\20_005
C:\FolderProviderTest\18_04_2012\20_005
C:\FolderProviderTest\18_04_2012\20_006
C:\FolderProviderTest\18_04_2012\20_006
C:\FolderProviderTest\18_04_2012\20_006
C:\FolderProviderTest\18_04_2012\20_007
C:\FolderProviderTest\18_04_2012\20_007
Press any key to continue
关注点
TreeFolderProvider
类实现了IFolderProvider
接口,该接口只有NextFolder
方法,让您有机会制定一些其他的文件夹提供策略(策略模式)。在示例程序中,提供了TrivialFolderProvider
,它总是返回相同的文件夹名称。当您需要测试您的程序并且不会有太多文件时,可以使用它。
历史
- 2012年4月18日 - 首次发布