ios - ubuntu檔案大小 - 顯示資料夾大小win10




如何計算文件夾的大小? (10)

我正在創建一個文件夾,用我的iPhone App緩存Documents中的圖像。 我希望能夠將此文件夾的大小保持在1MB,因此我需要檢查文件夾的大小(以字節為單位)。

我有代碼來計算文件大小 ,但我需要文件夾的大小。

最好的方法是什麼?


TL;博士

所有其他答案都關閉:)

問題

我想在這個老問題上加上我的兩分錢,因為似乎有許多答案都非常相似,但會產生非常不准確的結果。

要理解為什麼我們首先要定義文件夾大小 。 根據我的理解(可能是OP中的一個),它是包含其所有內容的目錄在卷上使用的字節數。 或者,換一種方式:

如果完全刪除目錄,則可用空間。

我知道這個定義並不是解釋這個問題的唯一有效方法,但我認為這是大多數用例歸結為的。

錯誤

現有答案都採用一種非常簡單的方法:遍歷目錄內容,累加(常規)文件的大小。 這並沒有考慮到一些細微之處。

  • 卷上使用的空間以為單位遞增,而不是以字節為單位。 即使是一個字節的文件也至少使用一個塊。
  • 文件攜帶元數據 (與任意數量的擴展屬性一樣)。 這些數據必須在某處。
  • HFS部署文件系統壓縮以使用比實際長度更少的字節來實際存儲文件。

所有這些原因使現有的答案產生了不恰當的結果。 所以我建議NSFileManager上的這個擴展(github上的代碼由於長度: SwiftObjective 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: NSFileManager 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)

使用枚舉塊更新了方法

僅使用文件計算文件夾大小

- (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;
}

不確定這是否對任何人有幫助,但我想談談我的一些發現(一些受@ zneak上述評論的啟發)。

  1. 我找不到使用NSDirectoryEnumerator任何快捷方式,以避免枚舉文件以獲取目錄的總包含大小。

  2. 對於我的測試,使用-[NSFileManager subpathsOfDirectoryAtPath:path error:nil]比使用-[NSFileManager enumeratorAtPath:path]更快。 這看起來像是一個典型的時間/空間權衡,因為subPaths...創建一個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)。


以下內容應該有助於您入門。 您需要將_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;
}

如果我們想獲得任何文件的大小,那麼這裡是一個方法,我們只需要傳遞該文件的路徑。

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

快速實施

class func folderSize(folderPath:String) -> UInt{

    // @see http://.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
}

我創建了一個簡單的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")

我在使用它之前清理了第一個答案的實現,所以它不再使用快速枚舉拋出棄用的警告+。

/**
 *  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;
}

這是一個快速的2.1 / 2.2答案,使用擴展並建立了Rok的答案:

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))

這是基於@vitalii擴展的Swift 3等效的FileManager擴展:

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
}}






nsfilemanager