在Windows上用Python快速计算文件夹大小

6 投票
6 回答
6943 浏览
提问于 2025-04-15 17:31

我在找一种快速的方法,用Python在Windows上计算一个文件夹的大小。到目前为止,我有以下代码:

def get_dir_size(path):
  total_size = 0
  if platform.system() == 'Windows':
    try:
      items = win32file.FindFilesW(path + '\\*')
    except Exception, err:
      return 0

    # Add the size or perform recursion on folders.
    for item in items:
      attr = item[0]
      name = item[-2]
      size = item[5]

      if (attr & win32con.FILE_ATTRIBUTE_DIRECTORY) and \
         not (attr & win32con.FILE_ATTRIBUTE_SYSTEM):  # skip system dirs
        if name not in DIR_EXCLUDES:
          total_size += get_dir_size("%s\\%s" % (path, name))

      total_size += size

  return total_size

但是当文件夹的大小超过100G时,这个方法就不够好了。有没有什么办法可以改进一下?

在一台性能不错的电脑上(2GHz以上,5G内存),我花了72秒来处理422GB的数据,里面有226,001个文件和12,043个文件夹。而使用资源管理器的属性选项则只需要40秒。

我知道我有点贪心,但我希望能找到一个更好的解决方案。

Laurent Luce

6 个回答

1

这个目录树真是庞大啊。正如其他人所说的,我不太确定你能否加快它的速度……没有数据的情况下是没办法的。这意味着……

如果你能以某种方式缓存数据(我不太清楚具体的含义是什么),那么你可能可以加快速度(我想……一如既往,测量、测量、再测量)。

我想我不需要告诉你怎么做缓存,你看起来对这方面挺了解的。而且我也不太清楚Windows上该怎么做。;-)

2

如果你使用os.walk,就不需要用递归算法了。可以看看这个问题

你应该对这两种方法的速度进行测试,不过这种方法应该会快很多:

import os

def get_dir_size(root):
    size = 0
    for path, dirs, files in os.walk(root):
        for f in files:
            size +=  os.path.getsize( os.path.join( path, f ) )
    return size
6

快速分析你的代码显示,超过90%的时间都花在了FindFilesW()这个调用上。这意味着,如果只是调整Python代码,得到的改进会非常有限。

一些小的调整(如果你继续使用FindFilesW)可以包括确保DIR_EXCLUDES是一个集合而不是列表,避免在其他模块上重复查找,以及懒惰地索引item[],还有把sys.platform的检查移到外面。这些改动和其他一些改动结合起来,但速度提升不会超过1-2%

DIR_EXCLUDES = set(['.', '..'])
MASK = win32con.FILE_ATTRIBUTE_DIRECTORY | win32con.FILE_ATTRIBUTE_SYSTEM
REQUIRED = win32con.FILE_ATTRIBUTE_DIRECTORY
FindFilesW = win32file.FindFilesW

def get_dir_size(path):
    total_size = 0
    try:
        items = FindFilesW(path + r'\*')
    except pywintypes.error, ex:
        return total_size

    for item in items:
        total_size += item[5]
        if (item[0] & MASK == REQUIRED):
            name = item[8]
            if name not in DIR_EXCLUDES:
                total_size += get_dir_size(path + '\\' + name)

    return total_size

唯一能带来显著速度提升的方法是使用不同的API或者不同的技术。你在评论中提到过要在后台进行这个操作,所以你可以考虑使用一些监控文件夹变化的包,来进行增量更新。可能可以使用FindFirstChangeNotification API或者类似的东西。你可以设置监控整个目录树,或者根据这个程序的工作方式(我没用过)来决定是否在整个树的不同子集上注册多个请求,这样可以减少你在收到通知后需要搜索的内容,以确定实际发生了什么变化以及现在的大小。

编辑:我在评论中问过你是否考虑到了Windows XP及以后的系统重度缓存文件系统元数据。我刚刚检查了你的代码(和我的代码)在Windows上的表现,选择了我C:\文件夹中的所有项目,然后按Alt-Enter打开属性窗口。在用你的代码执行一次后,耗时40秒,现在用两种方法都只需20秒。换句话说,你的代码实际上和Windows本身一样快,至少在我的机器上是这样。

撰写回答