programing

폴더의 크기를 계산하려면 어떻게 해야 합니까?

newnotes 2023. 9. 7. 22:03
반응형

폴더의 크기를 계산하려면 어떻게 해야 합니까?

iPhone App으로 Documents 내부의 이미지를 캐시할 폴더를 만들고 있습니다.저는 이 폴더의 크기를 1MB까지 유지할 수 있기를 원하기 때문에, 제 폴더의 크기를 바이트 단위로 확인해야 합니다.

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

이것을 하는 가장 좋은 방법은 무엇입니까?

tl;dr

다른 대답들은 모두 틀렸습니다 :)

문제

저는 이 오래된 질문에 저의 2센트를 보태고 싶습니다. 왜냐하면 모두 매우 비슷하지만 어떤 경우에는 매우 부정확한 결과를 낳는 많은 답변들이 있는 것 같기 때문입니다.

폴더의 크기를 먼저 정의해야 하는 이유를 이해하기 위해서입니다.제가 알기로는 (아마도 OP 중 하나일 것입니다) 그것은 모든 콘텐츠를 포함하는 디렉터리가 볼륨에서 사용하는 바이트의 양입니다.아니면, 다른 말로 하자면:

디렉토리가 완전히 제거될 경우 사용 가능한 공간입니다.

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

오류

기존의 답변들은 모두 매우 간단한 접근법을 취합니다. 디렉토리 내용을 이동하여 (정규) 파일 크기를 추가합니다.이것은 몇 가지 미묘한 부분을 고려하지 않습니다.

  • 볼륨에 사용되는 공간은 바이트 단위가 아닌 블록 단위로 증가합니다.하나의 바이트 파일이라도 적어도 하나의 블록을 사용합니다.
  • 파일은 메타 데이터(확장 속성의 수와 같이)를 운반합니다.이 데이터는 어딘가에 있어야 합니다.
  • HFS는 파일 시스템 압축을 구현하여 실제 길이보다 적은 바이트를 사용하여 파일을 실제로 저장합니다.

해결책

이 모든 이유는 기존의 답변이 부정확한 결과를 낳게 만듭니다. 나는 이 을 ㅇㅇㅇ에 NSFileManager(길이로 인한 GitHub 상의 코드: Swift 4, Objective C) 문제를 해결하기 위해서입니다.특히 파일이 많이 포함된 디렉토리의 경우 속도가 상당히 빠릅니다.

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

시험

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

테스트에서 나는 100개의 작은 파일(0에서 800바이트 사이)을 포함하는 디렉토리를 만듭니다.folderSize:은 총 나의른는총한는의안를에서 21 kB의안d는ybdallocatedSize는 401합니다. method는 401 kBΩ입니다.

증명

저는 그 결과를 확인했습니다.allocatedSize테스트 디렉토리를 삭제하기 전과 삭제한 후에 볼륨에서 사용 가능한 바이트의 차이를 계산함으로써 정확한 값에 더 가깝습니다.에서 그 Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ ΔΔ Δ,allocatedSize.

아직 개선의 여지가 있음을 이해하려면 Rob Napier의 코멘트를 참조하시기 바랍니다.

성능

하지만 또 다른 이점이 있습니다.할 때 6에서 1000개의 파일을 계산할 때 iPhone 6에서 1000개의 파일을 계산합니다.folderSize:은 약 되는 반면약 250 ms가 됩니다.allocatedSize는 동일한 계층을 35ms 안에 횡단합니다.

이것은 아마도 사용하기 때문일 것입니다.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)

알렉스, 당신이 많이 도와줘서 고맙군요. 이제 다음과 같은 기능을 써서 효과가 있습니다.

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

파인더와 마찬가지로 정확한 바이트 수를 제시하고 있습니다.

별도로 파인더는 두 개의 숫자를 반환합니다.하나는 디스크의 크기이고 다른 하나는 실제 바이트 수입니다.

예를 들어 폴더 중 하나에서 이 코드를 실행하면 130398의 '파일 크기'로 코드가 다시 나타납니다.파인더에 체크인해보니 디스크 사이즈가 201KB(130,398바이트)라고 나와있습니다.

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

만약 누군가가 이 다양한 숫자에 더 많은 정보를 추가할 수 있다면 감사하겠습니다.

건배.

이렇게 하면 폴더와 파일MB, KB, GB 단위 가져올있습니다. ---

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 {
        print(error)
    }
    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개의 디렉토리 크기를 구했는데, 1개의 디렉토리가 존재하지 않으면 제로 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];
}

다음은 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. 내 시험을 위해, 사용하기-[NSFileManager subpathsOfDirectoryAtPath:path error:nil]사용하는 것보다 빨랐습니다.-[NSFileManager enumeratorAtPath:path]. 제가 보기엔 고전적인 시공간의 균형일 수도 있습니다.subPaths...NSArray를 만들고, NSArray를 반복합니다.enumerator...아닐 수도 있습니다.

1위에 배경이.가정:

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

그리고나서

[fileMan enumeratorAtPath:dirPath] fileAttributes]

돌아온다nil. 올바른 속성 액세스자는directoryAttributes,그렇지만

[fileMan enumeratorAtPath:dirPath] directoryAttributes] fileSize]

포함된 모든 파일의 크기의 재귀적 합이 아닌 디렉토리 정보의 크기를 반환합니다(Finder의 ala ⌘-I).

간단한 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")

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

반응형