如何计算文件夹的大小?

jos*_*ney 66 iphone nsfilemanager ios

我正在创建一个文件夹,用我的iPhone App缓存Documents中的图像.我希望能够将此文件夹的大小保持在1MB,因此我需要检查文件夹的大小(以字节为单位).

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

最好的方法是什么?

Nik*_*uhe 68

TL;博士

所有其他答案都关闭:)

问题

我想在这个老问题上加上我的两分钱,因为似乎有许多答案都非常相似,但产生的结果在某些情况下是非常不准确的.

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

如果目录将被完全删除,则可用空间.

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

错误

现有答案都采用一种非常简单的方法:遍历目录内容,累加(常规)文件的大小.这不需要考虑一些细微之处.

  • 卷上使用的空间以为单位递增,而不是以字节为单位.即使是一个字节的文件也至少使用一个块.
  • 文件携带元数据(与任意数量的扩展属性一样).这些数据必须在某处.
  • HFS部署文件系统压缩以使用比实际长度更少的字节来实际存储文件.

所有这些原因使得现有答案产生了不恰当的结果.所以我建议这个扩展NSFileManager(github 上的代码由于长度:Swift 4,Objective C)来解决这个问题.它也快得多,特别是对于包含大量文件的目录.

该解决方案的核心是使用NSURLNSURLTotalFileAllocatedSizeKeyNSURLFileAllocatedSizeKey性的判定来检索文件的大小.

测试

我还建立了一个简单的iOS测试项目,展示了解决方案之间的差异.它显示了在某些情况下结果的完全错误.

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

证明

allocatedSize通过计算删除测试目录之前和之后卷上可用字节的差异,我确保结果更接近正确的值.在我的测试中,差异总是完全等于结果allocatedSize.

请参阅Rob Napier的评论,了解仍有改进的余地.

性能

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

这可能是由于使用了NSFileManager新的(ish)enumeratorAtURL:includingPropertiesForKeys:options:errorHandler:API来遍历层次结构.此方法允许您为要迭代的项指定预取属性,从而减少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)
Run Code Online (Sandbox Code Playgroud)

  • 非常有用的信息。有一个(非常)小问题可能将其与“如果目录被完全删除则可用空间”区分开来。如果存在来自树外部 inode 的任何硬链接,则删除这些硬链接实际上不会释放文件。硬链接并不是特别常见,在目录中不太常见,在 iOS 上更不常见,因此这是一个相当小的数量,并且不会削弱该技术的实用性。 (2认同)
  • @Isuru size参数是方法的实际结果,并通过引用返回(在Swift中使用有点尴尬).如果您不想简单地将方法移植到更快的版本,可以通过引用传递变量,如下所示:`var size = UInt64(0); nr_getAllocatedSize(&size,ofDirectoryAtURL:someURL)` (2认同)
  • @Keiwan我特此在MIT许可下发布. (2认同)

jos*_*ney 42

那个亚历克斯的干杯,你帮了很多忙,现在已经写了下面的功能,这就是诀窍......

- (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;
}
Run Code Online (Sandbox Code Playgroud)

它提出了Finder所做的确切字节数.

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

例如,当我在我的一个文件夹上运行此代码时,它返回代码中的'fileSize'为130398.当我检入Finder时,它表示磁盘上的大小为201KB(130,398字节).

我不确定这里有什么(201KB或130,398字节)作为实际尺寸.就目前而言,我会安全地将我的限制减半,直到我发现这意味着什么......

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

干杯,


The*_*ger 31

这是如何获得文件夹和文件sizeMB,KBGB ---

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;
}
Run Code Online (Sandbox Code Playgroud)

注意:如果是子文件夹,请使用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;
}
Run Code Online (Sandbox Code Playgroud)

---------- Swift 4.0 ----------

1.文件夹大小 -

func sizeOfFolder(_ folderPath: String) -> String? {
    do {
        let contents = try FileManager.default.contentsOfDirectory(atPath: folderPath)
        var folderSize: Int64 = 0
        for content in contents {
            do {
                let fullContentPath = folderPath + "/" + content
                let fileAttributes = try FileManager.default.attributesOfItem(atPath: fullContentPath)
                folderSize += fileAttributes[FileAttributeKey.size] as? Int64 ?? 0
            } catch _ {
                continue
            }
        }

        /// This line will give you formatted size from bytes ....
        let fileSizeStr = ByteCountFormatter.string(fromByteCount: folderSize, countStyle: ByteCountFormatter.CountStyle.file)
        return fileSizeStr

    } catch let error {
        print(error.localizedDescription)
        return nil
    }
}
Run Code Online (Sandbox Code Playgroud)

2.文件大小 -

func sizeOfFile(_ filePath: String) -> String? {
    do {
        let fileAttributes = try FileManager.default.attributesOfItem(atPath: filePath)
        let folderSize = fileAttributes[FileAttributeKey.size] as? Int64 ?? 0
        let fileSizeStr = ByteCountFormatter.string(fromByteCount: folderSize, countStyle: ByteCountFormatter.CountStyle.file)
        return fileSizeStr
    } catch {
        print(error)
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)


ndo*_*doc 30

在iOS 5中,该方法-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;
}
Run Code Online (Sandbox Code Playgroud)

  • 您当然也可以使用快速枚举:for(NSString*filesName in filesArray){} (5认同)

Ale*_*lds 10

以下内容应该有助于您入门.但是,您需要修改_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;
}
Run Code Online (Sandbox Code Playgroud)