Programing

폴더 크기는 어떻게 계산합니까?

lottogame 2020. 11. 13. 07:40
반응형

폴더 크기는 어떻게 계산합니까?


iPhone 앱을 사용하여 문서 내부에 이미지를 캐시 할 폴더를 만들고 있습니다. 이 폴더의 크기를 1MB로 줄이려면 폴더의 크기를 바이트 단위로 확인해야합니다.

파일 크기 를 계산하는 코드가 있지만 폴더 크기가 필요합니다.

이를 수행하는 가장 좋은 방법은 무엇입니까?


tl; dr

다른 모든 답변은 꺼져 있습니다. :)

문제

모두 매우 유사하지만 경우에 따라 매우 정확하지 않은 결과를 산출하는 많은 답변이있는 것처럼 보이므로이 오래된 질문에 2 센트를 더하고 싶습니다.

먼저 폴더크기 를 정의해야하는 이유를 이해 합니다 . 내 이해 (그리고 아마도 OP 중 하나)는 모든 내용을 포함하는 디렉토리가 볼륨에서 사용하는 바이트의 양입니다. 또는 다른 방식으로 입력하십시오.

디렉토리가 완전히 제거되면 사용할 수있는 공간입니다.

나는이 정의가 질문을 해석하는 유일한 유효한 방법이 아니라는 것을 알고 있지만 대부분의 사용 사례가 결론을 내리는 것이라고 생각합니다.

오류

기존 답변은 모두 매우 간단한 접근 방식을 취합니다. 디렉토리 내용을 탐색하여 (일반) 파일의 크기를 추가합니다. 이것은 몇 가지 미묘함을 고려하지 않습니다.

  • 볼륨에 사용 된 공간 은 바이트가 아닌 블록 단위로 증가 합니다. 1 바이트 파일도 하나 이상의 블록을 사용합니다.
  • 파일은 메타 데이터를 전달 합니다 (예 : 확장 된 속성의 수). 이 데이터는 어딘가에 있어야합니다.
  • HFS는 파일 시스템 압축배포 하여 실제 길이보다 적은 바이트를 사용하여 파일을 실제로 저장합니다.

해결책

이러한 모든 이유 때문에 기존 답변이 정확하지 않은 결과를 생성합니다. 그래서 문제를 해결하기 위해 ( NSFileManager길이로 인한 github의 코드 : Swift 4 , Objective C ) 에이 확장을 제안 하고 있습니다. 특히 많은 파일을 포함하는 디렉토리에서 상당히 빠릅니다.

솔루션의 핵심은 NSURLNSURLTotalFileAllocatedSizeKey또는 NSURLFileAllocatedSizeKey속성을 사용 하여 파일 크기를 검색하는 것입니다.

테스트

또한 솔루션 간의 차이점을 보여주는 간단한 iOS 테스트 프로젝트를 설정했습니다 . 일부 시나리오에서 결과가 얼마나 완전히 틀릴 수 있는지 보여줍니다.

테스트에서 100 개의 작은 파일 (0 ~ 800 바이트 범위)을 포함하는 디렉토리를 만듭니다. folderSize:내 동안 다른 않음 복사 방법은 21 kB의 합계를 산출 allocatedSize방법은 401 킬로바이트를 산출한다.

증명

allocatedSize테스트 디렉토리를 삭제하기 전과 후에 볼륨에서 사용 가능한 바이트의 차이를 계산하여 의 결과가 올바른 값에 더 가까운 지 확인했습니다 . 내 테스트에서 차이는 항상 allocatedSize.

여전히 개선의 여지가 있음을 이해하려면 Rob Napier의 의견을 참조하십시오.

공연

그러나 또 다른 장점이 있습니다. 1000 개의 파일이있는 디렉토리의 크기를 계산할 때 제 iPhone 6 folderSize:에서이 방법은 약 250ms가 걸리고 allocatedSize동일한 계층 구조를 35ms로 횡단합니다.

이는 아마도 NSFileManager의 new (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)

많은 도움을 주신 Alex에게 건배, 이제 트릭을 수행하는 다음 함수를 작성했습니다 ...

- (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는 두 개의 숫자를 반환합니다. 하나는 디스크의 크기이고 다른 하나는 실제 바이트 수입니다.

예를 들어 폴더 중 하나에서이 코드를 실행하면 'fileSize'가 130398 인 코드로 돌아옵니다. Finder를 체크인하면 크기가 디스크에서 201KB (130,398 바이트)라고 표시됩니다.

실제 크기로 여기 (201KB 또는 130,398 바이트)로 무엇을해야할지 잘 모르겠습니다. 지금은 안전한쪽으로 가서 이것이 정확히 무엇을 의미하는지 알 때까지 한계를 반으로 줄 이겠습니다.

누구든지이 다른 숫자에 더 많은 정보를 추가 할 수 있다면 감사하겠습니다.

건배,


이 폴더와 파일을 받으시는 방법 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;
}

참고 : 하위 폴더의 경우 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;
}

---------- 스위프트 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
    }
}

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 _ {
        return nil
    }
}

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

다음과 같은 것이 시작하는 데 도움이 될 것입니다. _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 개 디렉터리의 디렉터리 크기를 얻었습니다. 디렉터리 하나가 없으면 0KB가 표시됩니다. 그렇지 않으면 코드의 후반부에 각각 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의 답변을 기반으로 한 신속한 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;
}

다음은 @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
}}

성능면에서는 유닉스 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;
}

파일의 크기를 얻으려면 여기에 해당 파일의 경로 만 전달하면되는 메서드가 있습니다.

- (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. For my tests, using -[NSFileManager subpathsOfDirectoryAtPath:path error:nil] was faster than using -[NSFileManager enumeratorAtPath:path]. This looks to me like it might be a classic time/space tradeoff, as subPaths... creates an NSArray on which it then iterates, where enumerator... might not.

Some background on #1. Assuming:

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

Then

[fileMan enumeratorAtPath:dirPath] fileAttributes]

returns nil. The correct attribute accessor is directoryAttributes, but

[fileMan enumeratorAtPath:dirPath] directoryAttributes] fileSize]

returns the size of the directory information, not the recursive sum of the sizes of all contained files (a lá ⌘-I in Finder).


I've created a simple NSFileManager extension:

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

You can get the file size:

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

and the folder size:

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

참고URL : https://stackoverflow.com/questions/2188469/how-can-i-calculate-the-size-of-a-folder

반응형