计算一个文件夹的大小

只是一个快速的…

我正在创build一个文件夹来caching我的iPhone应用程序中的文件内的图像。 我希望能够保持这个文件夹的大小下降到1MB,所以我需要检查我的文件夹的字节大小。

我有代码来计算文件的大小,但我需要的文件夹的大小。

什么是最好的方法来做到这一点? 我是否需要遍历每个文件?

干杯

欢呼,亚历克斯,你帮了很多,现在写了下面的function,这个伎俩…

- (unsigned long long int)folderSize:(NSString *)folderPath { NSArray *filesArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil]; NSEnumerator *filesEnumerator = [filesArray objectEnumerator]; NSString *fileName; unsigned long long int fileSize = 0; while (fileName = [filesEnumerator nextObject]) { NSDictionary *fileDictionary = [[NSFileManager defaultManager] fileAttributesAtPath:[folderPath stringByAppendingPathComponent:fileName] traverseLink:YES]; fileSize += [fileDictionary fileSize]; } return fileSize; } 

它正如Finder一样提供确切的字节数。

另外,Finder返回两个数字。 一个是磁盘上的大小,另一个是实际的字节数。

例如,当我在其中一个文件夹上运行这个代码时,代码中的文件大小为130398。当我检查Finder时,它说磁盘上的大小是201KB(130,398字节)。

对于这里(201KB或130,398字节)的实际大小,我有点不确定。 现在,我会安全地把我的极限减半,直到我发现这意味着什么。

如果任何人可以添加更多的信息,这些不同的数字,我会很感激。

干杯,

TL;博士

所有其他的答案是closures:)

问题

我想在这个老问题上增加我的两分钱,因为似乎有很多答案都非常相似,但会产生非常不准确的结果。

要理解为什么我们首先必须定义文件夹大小 。 根据我的理解(也许是OP的一个),包含其所有内容的目录在卷上使用的字节数量。 或者换一种说法:

如果目录将被完全删除,则该空间变得可用。

我知道这个定义并不是解释这个问题的唯一有效方法,但我认为这是大多数用例归结到的。

错误

现有的答案都采取了一个非常简单的方法:遍历目录内容,加上(常规)文件的大小。 这并不需要考虑一些细节。

  • 卷上使用的空间以为单位递增,而不是以字节为单位。 即使是一个字节的文件也至less使用一个块。
  • 文件带有元数据 (如任何数量的扩展属性)。 这个数据必须去某个地方。
  • HFS部署文件系统压缩实际存储文件使用较less的字节,然后其实际长度。

所有这些原因使得现有的答案产生了不可思议的结果。 所以我在NSFileManager (github上的代码由于长度: Swift , Objective C )提出这个扩展来解决这个问题。 它也比较快,尤其是对于包含大量文件的目录。

解决scheme的核心是使用NSURLNSURLTotalFileAllocatedSizeKeyNSURLFileAllocatedSizeKey来检索文件大小。

testing

我还设置了一个简单的iOStesting项目 ,展示了解决scheme之间的差异。 它显示了在某些情况下结果是完全错误的。

在testing中,我创build了一个包含100个小文件(从0到800字节)的目录。 从其他答案复制的folderSize:方法计算总计21 kB,而我的allocatedSize方法产生401 kB。

certificate

通过计算删除testing目录前后卷上可用字节的差异,确保allocatedSize大小的结果更接近正确的值。 在我的testing中,差异总是与allocatedSize大小的结果完全相等。

请参阅Rob Napier的评论,了解还有改进的空间。

性能

但还有一个好处:当计算1000个文件目录的大小时,在我的iPhone 6上, folderSize:方法大约需要250毫秒,而allocatedSize的大小在35毫秒内遍历相同的层次结构。

这可能是由于使用NSFileManager的新(ish) enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: NSFileManager enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: API来遍历层次。 这个方法让你指定要迭代的项目的预取属性,从而导致更less的io。

结果

 Test `folderSize` (100 test files) size: 21 KB (21.368 bytes) time: 0.055 s actual bytes: 401 KB (401.408 bytes) Test `allocatedSize` (100 test files) size: 401 KB (401.408 bytes) time: 0.048 s actual bytes: 401 KB (401.408 bytes) Test `folderSize` (1000 test files) size: 2 MB (2.013.068 bytes) time: 0.263 s actual bytes: 4,1 MB (4.087.808 bytes) Test `allocatedSize` (1000 test files) size: 4,1 MB (4.087.808 bytes) time: 0.034 s actual bytes: 4,1 MB (4.087.808 bytes) 

在iOS 5中,不build议使用-filesAttributesAtPath:方法。 以下是使用新方法发布的第一个代码的版本:

 - (unsigned long long int)folderSize:(NSString *)folderPath { NSArray *filesArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil]; NSEnumerator *filesEnumerator = [filesArray objectEnumerator]; NSString *fileName; unsigned long long int fileSize = 0; while (fileName = [filesEnumerator nextObject]) { NSDictionary *fileDictionary = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:fileName] error:nil]; fileSize += [fileDictionary fileSize]; } return fileSize; } 

这是如何获得MBKBGB 文件夹和文件size

1.文件夹大小 –

 -(NSString *)sizeOfFolder:(NSString *)folderPath { NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:nil]; NSEnumerator *contentsEnumurator = [contents objectEnumerator]; NSString *file; unsigned long long int folderSize = 0; while (file = [contentsEnumurator nextObject]) { NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:file] error:nil]; folderSize += [[fileAttributes objectForKey:NSFileSize] intValue]; } //This line will give you formatted size from bytes .... NSString *folderSizeStr = [NSByteCountFormatter stringFromByteCount:folderSize countStyle:NSByteCountFormatterCountStyleFile]; return folderSizeStr; } 

注意:如果是子文件夹,请使用subpathsOfDirectoryAtPath:而不是contentsOfDirectoryAtPath:

2.文件大小 –

 -(NSString *)sizeOfFile:(NSString *)filePath { NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; NSInteger fileSize = [[fileAttributes objectForKey:NSFileSize] integerValue]; NSString *fileSizeStr = [NSByteCountFormatter stringFromByteCount:fileSize countStyle:NSByteCountFormatterCountStyleFile]; return fileSizeStr; } 

像下面的东西应该帮助你开始。 您需要将_documentsDirectory修改为您的特定文件夹,但是:

 - (unsigned long long int) documentsFolderSize { NSFileManager *_manager = [NSFileManager defaultManager]; NSArray *_documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *_documentsDirectory = [_documentPaths objectAtIndex:0]; NSArray *_documentsFileList; NSEnumerator *_documentsEnumerator; NSString *_documentFilePath; unsigned long long int _documentsFolderSize = 0; _documentsFileList = [_manager subpathsAtPath:_documentsDirectory]; _documentsEnumerator = [_documentsFileList objectEnumerator]; while (_documentFilePath = [_documentsEnumerator nextObject]) { NSDictionary *_documentFileAttributes = [_manager fileAttributesAtPath:[_documentsDirectory stringByAppendingPathComponent:_documentFilePath] traverseLink:YES]; _documentsFolderSize += [_documentFileAttributes fileSize]; } return _documentsFolderSize; } 

我用这个代码来获取2个目录的目录大小,如果一个目录不存在,它会显示0 KB。 否则,代码的后半部分将分别显示文件夹大小以及KB,MB,GB,并且还将以干净的格式显示它: 10.02 MB

尝试这样的事情:

 - (unsigned long long int)folderSize:(NSString *)folderPath { NSArray *filesArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil]; NSEnumerator *filesEnumerator = [filesArray objectEnumerator]; NSString *fileName; unsigned long long int fileSize = 0; while (fileName = [filesEnumerator nextObject]) { NSDictionary *fileDictionary = [[NSFileManager defaultManager] fileAttributesAtPath:[folderPath stringByAppendingPathComponent:fileName] traverseLink:YES]; fileSize += [fileDictionary fileSize]; } return fileSize; } -(NSString *)getMPSize { NSString*sizeTypeW = @"bytes"; int app = [self folderSize:@"/PathToTheFolderYouWantTheSizeOf/"]; NSFileManager *manager = [NSFileManager defaultManager]; if([manager fileExistsAtPath:@"/AnotherFolder/"] == YES){ int working = [self folderSize:@"/AnotherFolder/"]; if(working<1){ return @"Size: Zero KB"; }else{ if (working > 1024) { //Kilobytes working = working / 1024; sizeTypeW = @" KB"; } if (working > 1024) { //Megabytes working = working / 1024; sizeTypeW = @" MB"; } if (working > 1024) { //Gigabytes working = working / 1024; sizeTypeW = @" GB"; } return [NSString stringWithFormat:@"App: %i MB, Working: %i %@ ",app/1024/1024, working,sizeTypeW]; } }else{ return [NSString stringWithFormat:@"App: %i MB, Working: Zero KB",app/1024/1024]; } [manager release]; } 

下面是使用扩展和Rok的答案构build的2.1 / 2.2答案:

 extension NSFileManager { func fileSizeAtPath(path: String) -> Int64 { do { let fileAttributes = try attributesOfItemAtPath(path) let fileSizeNumber = fileAttributes[NSFileSize] let fileSize = fileSizeNumber?.longLongValue return fileSize! } catch { print("error reading filesize, NSFileManager extension fileSizeAtPath") return 0 } } func folderSizeAtPath(path: String) -> Int64 { var size : Int64 = 0 do { let files = try subpathsOfDirectoryAtPath(path) for i in 0 ..< files.count { size += fileSizeAtPath((path as NSString).stringByAppendingPathComponent(files[i]) as String) } } catch { print("error reading directory, NSFileManager extension folderSizeAtPath") } return size } func format(size: Int64) -> String { let folderSizeStr = NSByteCountFormatter.stringFromByteCount(size, countStyle: NSByteCountFormatterCountStyle.File) return folderSizeStr } } 

用法示例:

 let fileManager = NSFileManager.defaultManager() let documentsDirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] let dirSize: String = fileManager.format(fileManager.folderSizeAtPath(documentsDirPath)) 

更新方法使用枚举块

只用文件计算文件夹大小

 - (NSString *)sizeOfFolder:(NSString *)folderPath { NSArray *folderContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error:nil]; __block unsigned long long int folderSize = 0; [folderContents enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:obj] error:nil]; folderSize += [[fileAttributes objectForKey:NSFileSize] intValue]; }]; NSString *folderSizeStr = [NSByteCountFormatter stringFromByteCount:folderSize countStyle:NSByteCountFormatterCountStyleFile]; return folderSizeStr; } 

使用文件夹中的其他子目录计算文件夹大小

  NSArray *folderContents = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil]; 

获取文件大小

 - (NSString *)sizeOfFile:(NSString *)filePath { NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; NSInteger fileSize = [[fileAttributes objectForKey:NSFileSize] integerValue]; NSString *fileSizeString = [NSByteCountFormatter stringFromByteCount:fileSize countStyle:NSByteCountFormatterCountStyleFile]; return fileSizeString; } 

我认为使用Unix C方法对性能更好。

 + (long long) folderSizeAtPath: (const char*)folderPath { long long folderSize = 0; DIR* dir = opendir(folderPath); if (dir == NULL) return 0; struct dirent* child; while ((child = readdir(dir))!=NULL) { if (child->d_type == DT_DIR && child->d_name[0] == '.' && (child->d_name[1] == 0 // ignore . || (child->d_name[1] == '.' && child->d_name[2] == 0) // ignore dir .. )) continue; int folderPathLength = strlen(folderPath); char childPath[1024]; // child stpcpy(childPath, folderPath); if (folderPath[folderPathLength-1] != '/'){ childPath[folderPathLength] = '/'; folderPathLength++; } stpcpy(childPath+folderPathLength, child->d_name); childPath[folderPathLength + child->d_namlen] = 0; if (child->d_type == DT_DIR){ // directory folderSize += [self _folderSizeAtPath:childPath]; // // add folder size struct stat st; if (lstat(childPath, &st) == 0) folderSize += st.st_size; } else if (child->d_type == DT_REG || child->d_type == DT_LNK){ // file or link struct stat st; if (lstat(childPath, &st) == 0) folderSize += st.st_size; } } return folderSize; } 

这是基于@vitalii扩展名的FileManager扩展的Swift 3等价物:

 extension FileManager { func fileSizeAtPath(path: String) -> Int64 { do { let fileAttributes = try attributesOfItem(atPath: path) let fileSizeNumber = fileAttributes[FileAttributeKey.size] as? NSNumber let fileSize = fileSizeNumber?.int64Value return fileSize! } catch { print("error reading filesize, NSFileManager extension fileSizeAtPath") return 0 } } func folderSizeAtPath(path: String) -> Int64 { var size : Int64 = 0 do { let files = try subpathsOfDirectory(atPath: path) for i in 0 ..< files.count { size += fileSizeAtPath(path:path.appending("/"+files[i])) } } catch { print("error reading directory, NSFileManager extension folderSizeAtPath") } return size } func format(size: Int64) -> String { let folderSizeStr = ByteCountFormatter.string(fromByteCount: size, countStyle: ByteCountFormatter.CountStyle.file) return folderSizeStr }} 

如果我们想得到任何文件的大小,那么这里是一个方法,我们只需要传递该文件的path。

 - (unsigned long long int) fileSizeAt:(NSString *)path { NSFileManager *_manager = [NSFileManager defaultManager]; return [[_manager fileAttributesAtPath:path traverseLink:YES] fileSize]; } 

在使用它之前,我清理了一下第一个答案的实现,所以它不再抛出不推荐的警告+使用快速枚举。

 /** * Calculates the size of a folder. * * @param folderPath The path of the folder * * @return folder size in bytes */ - (unsigned long long int)folderSize:(NSString *)folderPath { NSFileManager *fm = [NSFileManager defaultManager]; NSArray *filesArray = [fm subpathsOfDirectoryAtPath:folderPath error:nil]; unsigned long long int fileSize = 0; NSError *error; for(NSString *fileName in filesArray) { error = nil; NSDictionary *fileDictionary = [fm attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:fileName] error:&error]; if (!error) { fileSize += [fileDictionary fileSize]; }else{ NSLog(@"ERROR: %@", error); } } return fileSize; } 

快速实施

 class func folderSize(folderPath:String) -> UInt{ // @see http://stackoverflow.com/questions/2188469/calculate-the-size-of-a-folder let filesArray:[String] = NSFileManager.defaultManager().subpathsOfDirectoryAtPath(folderPath, error: nil)! as [String] var fileSize:UInt = 0 for fileName in filesArray{ let filePath = folderPath.stringByAppendingPathComponent(fileName) let fileDictionary:NSDictionary = NSFileManager.defaultManager().attributesOfItemAtPath(filePath, error: nil)! fileSize += UInt(fileDictionary.fileSize()) } return fileSize } 

不知道这是否有助于任何人,但我想要涉及我的一些发现(一些灵感来自上面的@ zneak的评论)。

  1. 我找不到使用NSDirectoryEnumerator任何快捷方式,以避免通过文件枚举来获取目录的总包含大小。

  2. 对于我的testing,使用-[NSFileManager subpathsOfDirectoryAtPath:path error:nil]比使用-[NSFileManager enumeratorAtPath:path]更快。 这看起来像它可能是一个经典的时间/空间折衷,因为subPaths...创build一个NSArray,然后迭代,其中enumerator...可能不会。

#1的一些背景。 假设:

 NSFileManager *fileMan = [NSFileManager defaultManager]; NSString *dirPath = @"/"; // references some directory 

然后

 [fileMan enumeratorAtPath:dirPath] fileAttributes] 

返回nil 。 正确的属性访问器是directoryAttributes ,但是

 [fileMan enumeratorAtPath:dirPath] directoryAttributes] fileSize] 

返回目录信息的大小,而不是所有包含文件(Finder中的lá⌘-I)的recursion总和。

我创build了一个简单的NSFileManager扩展:

 extension NSFileManager { func fileSizeAtPath(path: String) -> Int { return attributesOfItemAtPath(path, error: nil)?[NSFileSize] as? Int ?? 0 } func folderSizeAtPath(path: String) -> Int { var size = 0 for file in subpathsOfDirectoryAtPath(path, error: nil) as? [String] ?? [] { size += fileSizeAtPath(path.stringByAppendingPathComponent(file)) } return size } } 

你可以得到文件大小:

 NSFileManager.defaultManager().fileSizeAtPath("file path") 

和文件夹大小:

 NSFileManager.defaultManager().folderSizeAtPath("folder path")