views:

976

answers:

5

Right now I'm using this code to get the size of a folder:

NSArray *contents;
     NSEnumerator *enumerator;
     NSString *path;
     contents = [[NSFileManager defaultManager] subpathsAtPath:folderPath];
     enumerator = [contents objectEnumerator];
     while (path = [enumerator nextObject]) {
      NSDictionary *fattrib = [[NSFileManager defaultManager] fileAttributesAtPath:[folderPath stringByAppendingPathComponent:path] traverseLink:YES];
      fileSize +=[fattrib fileSize];
     }

     [contents release];
     [path release];

The problem is that its highly innacurate. It either adds a few megabytes or deducts a few megabytes from the actual size. For example I got the file size of an .app bundle and this method reported 16.2MB, whereas the actual thing is 15.8.

What's the best way to get the size of a folder?

Thanks

+1  A: 

This is typically how it is done. 2 possibilities:

  1. Check your byte -> megabyte conversion routines. Also, do you want megabytes or mebibytes? (It probably depends on what you're comparing it to.)

  2. Try passing NO for the traverseLink parameter. There might very well be a symlink in the bundle pointing to something else that the routine you're comparing it to won't account for. You'll either count something in the bundle twice, or you'll include something outside the bundle entirely (most likely the former).

kperryua
+3  A: 

The documentation for fileSize states it does not include the size of a resource fork. You may need to use the Carbon File Manager API to reliably calculate directory sizes.

dreamlax
Ah yes, that's another possibility. I forgot about that.
kperryua
+2  A: 

I needed to do this today myself, and I've found that the code in this post on the Cocoa-dev list is super fast and matches what Finder says to the byte. (don't forget to OR in the kFSCatInfoRsrcSizes flag so you get resource fork sizes, too!)

If you need more explanation on how to use it, just leave a comment and I'll edit this post. =)

Dave DeLong
Thanks, what's a good way to convert an NSString containing a path to an FSRef?Thanks
macatomy
Just answered your question. =)
Dave DeLong
A: 

I just wanted to second Dave DeLong's suggestion about the post on Cocoa-dev, but add a cautionary note to be sure to read all the posts in the thread. There is one by Rosyna that's particularly worth noting. In my case I followed that advice (changing max items per fetch to 40) and saw a speed jump as well as the end to a nasty crashing bug.

Ira Cooke
Might be better to add this as a comment to his answer so they stay related even as their positions on the page change.
Quinn Taylor
I'm having problems with Dave DeLong's method. I use this code to convert my NSString into an FSRef:FSRef f; OSStatus os_status = FSPathMakeRef((const UInt8 *)[filePath fileSystemRepresentation], if (os_status == noErr) { NSLog(@"Success"); }And then I try to run the method:[self fastFolderSizeAtFSRef:f];However I get the error "incompatible type for argument 1 of 'fastFolderSize'"Any ideas ? Thanks
macatomy
A: 

I know that this is an old topic. But for anyone out there looking for answers on how to do this,

[[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir];
        if (isDir) {
            NSPipe *pipe = [NSPipe pipe];
            NSTask *t = [[[NSTask alloc] init] autorelease];
            [t setLaunchPath:@"/usr/bin/du"];
            [t setArguments:[NSArray arrayWithObjects:@"-k", @"-d", @"0", path, nil]];
            [t setStandardOutput:pipe];
            [t setStandardError:[NSPipe pipe]];

            [t launch];

            [t waitUntilExit];

            NSString *sizeString = [[[NSString alloc] initWithData:[[pipe fileHandleForReading] availableData] encoding:NSASCIIStringEncoding] autorelease];
            sizeString = [[sizeString componentsSeparatedByString:@" "] objectAtIndex:0];
            bytes = [sizeString longLongValue]*1024;
        }
        else {
            bytes = [[[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil] fileSize];
        }

It will use terminal to determine a size for folders in bytes. And it will use Cocoa's built in NSFileManager to get the size of files. It's very fast, and gets the exact size that finder reports.

Alex Zielenski
while this will certainly work, you have the performance hit of creating another process (which can be pretty expensive), or using `NSFileManager`, which doesn't include resource forks when calculating sizes.
Dave DeLong