65.9K
CodeProject 正在变化。 阅读更多。
Home

将大量文件写入 Windows 上的 HDD

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2012年4月18日

CPOL

3分钟阅读

viewsIcon

20746

downloadIcon

299

一个文件夹中包含的文件过多会导致文件操作减慢程序的速度。这里提供的类旨在为文件写入提供一个树状文件夹结构。

引言

如果您向一个文件夹写入大约一千个文件,然后尝试用资源管理器打开这个文件夹,您可能会注意到资源管理器开始变慢。如果您放入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日 - 首次发布
© . All rights reserved.