I've tried the NSTask > NSData method, but the CPU/memory overhead is extremely large for anything over 1GB, so I need to find a way to do this like, say, an FTP server does it.
EDIT: How does remote desktop's copy files do it?
I think I got it. I had to read it into the memory in small byte-size (HAHA GET THE PUN?) pieces and transfer it over that way. Keep in mind that this only works for files, not directories. I tested it on a 450MB file and it copied in about 3 minutes with the exact same byte count as the source. It was a video, and while I was streaming it to the client, I was able to play it as well. Nifty, huh?
Without further ado, here's the code I used, slightly patched up to do a simple file-copy instead of over the network.
[[NSFileManager defaultManager] createFileAtPath:#"/path/to/file/dest" contents:nil attributes:nil];
NSFileHandle *output = [NSFileHandle fileHandleForWritingAtPath:#"/path/to/file/dest"];
uint64 offset = 0;
uint32 chunkSize = 8192;
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:#"/path/to/file/source"];
NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];
NSData *data = [handle readDataOfLength:chunkSize];
NSLog(#"Entering Loop.");
while ([data length] > 0) {
[output seekToEndOfFile];
[output writeData:data];
offset += [data length];
NSLog(#"Switching Loop.");
[autoreleasePool release];
autoreleasePool = [[NSAutoreleasePool alloc] init];
[handle seekToFileOffset:offset];
data = [handle readDataOfLength:chunkSize];
}
NSLog(#"Exited Loop.");
[handle closeFile];
[autoreleasePool release];
[output closeFile];
[output release];
Related
My Problem :- NSURLSession does not release previous call API Memory of 5MB Chunk
I am calling APIs in do while loop to upload 500MB video. I have to send every 5MB chunk with different APIs not in one API.
For Example 500MB Video and create 100 chunks and send using NSURLSession so calls 100 times but NSURLSession does not release previous call API Memory of 5MB Chunk
(1) I have created 5MB Chunk.
(2) read File using NSFileHandle with 5MB Chunk using OffSet
(3) change URL for all chunk and call api (necessary to send all chunk at different URL)
I do not want convert video in NSData in (500MB) i want to send chunk through API
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
//dispatch_group_t group = dispatch_group_create();
__block NSUInteger counterFailure = 0; // PSM Anks calling blob by url fails 4 time, exit for funtion
arrBlobIds = [[NSMutableArray alloc]init];
__block NSInteger intBlockIdCount = 100000; // PSM Anks blobid to assign id to every blob
__block NSUInteger offset = 0; // PSM Anks offset to start posution to read data
NSUInteger length = [[[NSFileManager defaultManager] attributesOfItemAtPath:[urlOfGallaryVideo path] error:nil] fileSize]; // PSM anks total lenght of media
NSUInteger intChunkSize = (5000 * 1024); // PSM anks chunk size
while (offset < length){
//dispatch_group_enter(group);
NSLog(#"offset 1 : %lu",(unsigned long)offset);
// PSM Anks Creat Chunk from file according to length
NSUInteger intThisChunkSize = length - offset > intChunkSize ? intChunkSize : length - offset;
//NSData* chunk = [NSData dataWithBytesNoCopy:(char *)[myBlob bytes] + offset length:intThisChunkSize freeWhenDone:NO];
__block NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:[urlOfGallaryVideo path]];
[fileHandle seekToFileOffset:offset];
__block NSData *dataChunk = [fileHandle readDataOfLength:intThisChunkSize];
// PSM Anks Convert block id in Base 64 encode
NSData *dataBlockId = [[NSString stringWithFormat:#"%ld",intBlockIdCount] dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64BlockId = [dataBlockId base64EncodedStringWithOptions:0];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#&comp=block&blockid=%#",[dictAzureSAS objectForKey:#"uri"],base64BlockId]]];
[request setHTTPMethod:#"PUT"];
[request setHTTPBody:dataChunk];
//[request setValue:[NSString stringWithFormat:#"%lu",(unsigned long)dataChunk.length] forHTTPHeaderField:#"Content-Length"]; // Do not need
//[request setValue:strVideoMIMEType forHTTPHeaderField:#"Content-Type"]; // Do not need
[request addValue:#"BlockBlob" forHTTPHeaderField:#"x-ms-blob-type"];
//NSLog(#"request : %#",request);
//NSLog(#"dataChunk.length : %lu \n url for blob %# \n request %#",(unsigned long)dataChunk.length,[NSURL URLWithString:[NSString stringWithFormat:#"%#&comp=block&blockid=%#",[dictAzureSAS objectForKey:#"uri"],base64BlockId]],request);
NSLog(#"dataChunk.length : %lu",(unsigned long)dataChunk.length);
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.URLCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
config.timeoutIntervalForRequest = 20.0;
config.URLCredentialStorage = nil;
config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
///NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
config = nil;
//NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *dataTaskForUpload = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(#"Finished with status code: %li", (long)[(NSHTTPURLResponse *)response statusCode]);
NSLog(#"response: %#", response);
NSLog(#"error: %# %#", error,error.description);
if(data != nil) // PSM anks Check Data is nil otherwise app crashed
{
NSMutableArray *jsonList = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
NSLog(#"jsonList: %#", jsonList);
}
dataChunk = nil;
fileHandle = nil;
if(error == nil)
{
/*
// PSM Anks First Add Then increment
[arrBlobIds addObject:base64BlockId];
intBlockIdCount++;
offset += intThisChunkSize;
counterFailure = 0;
*/
}
else
{
/*
counterFailure++;
offset = intThisChunkSize;
if(counterFailure >= 4)
{
NSLog(#"Enter counter Failure %lu",(unsigned long)counterFailure);
counterFailure = 0;
[self stopLoader];
[CommonAlertViewMsgs cannotConnectServer:self];
return ;
}
*/
}
//dispatch_group_leave(group);
dispatch_semaphore_signal(semaphore);
[session finishTasksAndInvalidate];
}];
[dataTaskForUpload resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(#"offset 2 : %lu",(unsigned long)offset);
}
Your problem is probably that your NSData objects are being put in an autorelease pool, which is never getting drained until after your main dispatch_async block completes. You can probably fix the immediate problem by adding an #autoreleasepool to your while loop; i.e.
while (offset < length) #autoreleasepool {
However, your dispatch_semaphore_wait at the end is blocking a dispatch queue, which is generally discouraged. What I would recommend would be, in addition to adding the #autoreleaspool to the while loop, to use a dispatch group instead of the semaphore, and to use dispatch_group_notify at the end instead of dispatch_group_wait. This will cause your main dispatch_async block to complete, which will release any autoreleased objects which have accumulated in it, and then the block you pass to dispatch_group_notify will be called once all your operations are complete.
EDIT: Knowing a little more about what you're trying to do, here is an alternative that will run the processes one at a time, while still not blocking the dispatch queue:
(pseudocode)
- (void)sendRequestWithOffset:length:otherParameters: {
send the url request {
do what you do
if newOffset < length {
[self sendRequestWithOffset:newOffset length:length otherParameters:whatever];
} else {
hooray, we're done
}
}
}
It's sort of like a recursive call (but not really, since we won't accumulate stack frames). Basically it's an asyncronous version of your while loop; your tasks occur one at a time, there's no blockage of dispatch queues, and since each dispatch queue has its own autorelease pool, you won't get buildup of autoreleased objects either and your memory usage should stay reasonable.
If you want to minimize your memory footprint while the uploads run, you should:
Try #autoreleasepool as advised by Charles;
Reuse one NSURLSession rather than recreating a new NSURLSession each time inside your loop; and
Use file-based upload tasks (e.g. uploadTaskWithRequest:fromFile:) rather than dataTask.
Note, with file-based upload tasks, you can still configure your NSURLRequest like you are now, but don't set httpBody, but instead supply that as the fromFile parameter of the above method.
Once you have this memory issue behind you, you can pursue other approaches to improve performance, too, but let's not get ahead of ourselves.
I am using a library called objective zip to extract a file.
the relevant code:
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
ZipFile *unzipFile= [[ZipFile alloc] initWithFileName:requestFinishedPresentationPath mode:ZipFileModeUnzip];
NSArray *infos= [unzipFile listFileInZipInfos];
for (FileInZipInfo *info in infos) {
NSLog(#"- %# %# %d (%d)", info.name, info.date, info.size, info.level);
[unzipFile locateFileInZip:info.name];
// Expand the file in memory
ZipReadStream *read= [unzipFile readCurrentFileInZip];
NSMutableData *data = [[NSMutableData alloc] initWithLength:256];
int bytesRead = [read readDataWithBuffer:data];
[data writeToFile:[documentsDir stringByAppendingPathComponent:info.name] atomically:NO];
NSLog([documentsDir stringByAppendingPathComponent:info.name]);
[read finishedReading];
}
[unzipFile close];
the first nslog outputs the following:
- _some-path_/modules/reference-popup/ref-popup.js 2012-08-21 08:49:36 +0000 109 (-1)
the second nslog outputs the follows:
/Users/_USER_/Library/Application Support/iPhone Simulator/5.1/Applications/F2649DB7-7806-4C49-8DF9-BD939B2A9D5A/Documents/_some-path_/modules/slide-popup/slide-popup.css
Basically i think my read and data objects are not talking to each other... when I navigate to the right directory in my browser, all it does is show the two files, which are supposed to be directories (they are the most top-level directories) but the browser says they were written out as 1kb files. I try and view the file and its a 256 byte file... as per the buffer in the code.
What should I do instead?
To expand a zip (with all the files into a directory properly)?
Calling readDataWithBuffer will fill up the buffer (up to its length) and then pause the stream. You should actually put it in a loop and stop when bytesRead is 0. This is to allow expanding files bigger than available memory (think of a video, for example).
Also, consider that zip files have no directory structure: directories are simply embedded as part of the zipped file names. It's up to you to recreate the correct path. Usually you have some base directory, append the zipped file name and create the file with the resulting complete path.
In the Objective-Zip wiki, at the end, you can find a section titled Memory Management where a sample loop is present and commented. A more completed example is the following:
// Open zip descriptor
ZipFile *zip= [[ZipFile alloc] initWithFileName:zipPath mode:ZipFileModeUnzip];
NSMutableData *buffer= [[NSMutableData alloc] initWithLength:BUFFER_SIZE];
// Loop on file list
NSArray *zipContentList= [zip listFileInZipInfos];
for (FileInZipInfo *fileInZipInfo in zipContentList) {
// Check if it's a directory
if ([fileInZipInfo.name hasSuffix:#"/"]) {
NSString *dirPath= [documentsPath stringByAppendingPathComponent:fileInZipInfo.name];
[[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:NULL];
continue;
}
// Create file
NSString *filePath= [documentsPath stringByAppendingPathComponent:fileInZipInfo.name];
[[NSFileManager defaultManager] createFileAtPath:filePath contents:[NSData data] attributes:nil];
NSFileHandle *file= [NSFileHandle fileHandleForWritingAtPath:filePath];
// Seek file in zip
[zip locateFileInZip:fileInZipInfo.name];
ZipReadStream *readStream= [zip readCurrentFileInZip];
// Reset buffer
[buffer setLength:BUFFER_SIZE];
// Loop on read stream
int totalBytesRead= 0;
do {
int bytesRead= [readStream readDataWithBuffer:buffer];
if (bytesRead > 0) {
// Write data
[buffer setLength:bytesRead];
[file writeData:buffer];
totalBytesRead += bytesRead;
} else
break;
} while (YES);
// Close file
[file closeFile];
[readStream finishedReading];
}
// Close zip and release buffer
[buffer release];
[zip close];
[zip release];
Hope this helps.
I am trying to downloading more then 600 images in loop with a progress meter on the top of the screen to the user. I blocked my screen with a fade layer for showing activity and progress.
I am getting the memory warning message in between and app getting crashes.
My steps to reach the loop are :
On app delegate, I check first core data table for all rows which is having "0" value in isImageAvailable bool field.
If shows me some count (say 600), and I show and alert with YES and NO option.
On YES : [self performSelector:#selector(myDownload:) withObject:nil afterDelay:0.2];
in myDownload
NSOperationQueue *queue = [NSOperationQueue new];
// Create our NSInvocationOperation to call loadDataWithOperation, passing in nil
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(startUpdatingRecords:) object:nil];
// Add the operation to the queue
[queue addOperation:operation];
[operation release];
[queue release];
in startUpdatingRecords :
-(void)startUpdatingRecords:(id)sender
{
[self performSelectorInBackground:#selector(updateProgressMeter:) withObject: [NSString stringWithFormat:#"%d",self.loopStartIndex]];
// Variable declarations
CGSize newSizeLarge ;
NSPredicate *predicate;
NSMutableArray *MatchingID;
Image_DB *data;
// Cache Directory path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSData *responseData; // = [[NSData alloc]init] ;
NSURL *url = [[[NSURL alloc]init] autorelease];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc]init] autorelease];
UIImage *imgSelected_Large = [[[UIImage alloc]init] autorelease];
// Loop through all IDs
for (int i = 0; i < [self.arrayOfID count]; i++) //for (int i = loopStart; i < loopEnd; i++)
{
if (self.abortDownload)
{
break;
}
NSString *documentsDirectory = [[[NSString alloc] initWithFormat:#"%#",[paths objectAtIndex:0]] autorelease];
documentsDirectory = [paths objectAtIndex:0];
documentsDirectory = [documentsDirectory stringByAppendingFormat:#"/ImageFolder"]; // Image folder path
myClass *classObj = [self.arrayOfID objectAtIndex:i];
NSString *strURl = [[[NSString alloc] initWithFormat:#"%#%#", self.MyURL,recipeObj.recipeImageStr] autorelease];
//NSLog(#"URL = %#",strURl);
url = [NSURL URLWithString:strURl];
request = [NSMutableURLRequest requestWithURL:url];
responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL]; // Get Image Data into NSData
//imgSelected_Large = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:strURl]]];
NSLog(#"Download Count = %d",i+1);
if (responseData != nil)
{
imgSelected_Large = [UIImage imageWithData:responseData];
// Resizining image
newSizeLarge.width = 320;
newSizeLarge.height = 180;
imgSelected_Large = [self imageWithImage:imgSelected_Large scaledToSize:newSizeLarge]; // New sized image
NSData *dataPhoto; // no need to release it because UIImageJPEGRepresentation gives autoreleased NSData obj.
dataPhoto = UIImageJPEGRepresentation(imgSelected_Large, 0.6); // Set new image representation and its Compression Quality
documentsDirectory = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"Image_%d", classObj.nodeID]];
[dataPhoto writeToFile:documentsDirectory atomically:YES]; //Write file to local folder at default path
predicate = [NSPredicate predicateWithFormat: #"(image_ID = %d )",recipeObj.nodeID];
MatchingID = [CoreDataAPIMethods searchObjectsInContext:#"Image_DB" :predicate :#"image_ID" :YES :self.managedObjectContext];
// Setting flag variable for available image
for (int j = 0; j< [MatchingID count]; j++)
{
//Assign the Authors Records in Class Object and save to Database
data = (Image_DB*) [MatchingID objectAtIndex:j];
// data.image_large = dataPhoto; // Code for storing BLOB object to DB
data.extra_1 = #"1";
//NSLog(#"Flag updated");
}
}
// Exit out code
if ( i == [self.arrayOfID count] - 1 || i == [self.arrayOfID count]) // Its the last record to be stored
{
NSError *error;
if (![self.managedObjectContext save:&error])
{
// Handle the error...
NSLog(#"Error in updating %#",error);
}
self.isUpdateImageCalled = NO;
[self performSelectorOnMainThread:#selector(removeProgressMeter) withObject:nil waitUntilDone:NO];
}
// Update UI screen while in downloading process
[self performSelectorInBackground:#selector(updateProgressMeter:) withObject:[NSString stringWithFormat:#"%d",self.loopStartIndex+i+1]];
}
}
If I didn't release responseData then my app shows me memory warning and got crashed. If I released then, [NSConcreteMutableData release]: message sent to deallocated instance 0x1e931de0 error occures.
How to refine my code. Can any one suggest me on my code and rework and make a refined code.
Please please help me out.
Your responseData returned by sendSynchronousRequest is autoreleased thus you shouldn't release it yourself. For the first sight I don't see a memory leak in your code. It is possible that your application actually uses too much memory, without leaking it. Try to place an autorelease pool inside your for cycle:
for (...) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// your original code with a lot of autoreleased objects
[pool release];
}
If you wrap your code within an autorelease pool, all objects that are sent the autorelease message inside the wrap will be actually released when the pool itself is released: this way you purge the memory in every for cycle.
See also Using Autorelease Pools in the doc, it specifically mentions that you should use them in the case "if you write a loop that creates many temporary objects".
I am erasing audio file using following code:
NSMutableData *wave =[NSMutableData dataWithContentsOfURL:self.recordedFileURL options:NSDataReadingUncached error:nil];
NSUInteger length = [wave length];
Byte *byteData = (Byte*)malloc(length);
memcpy(byteData, [wave bytes], length);
NSMutableData *data = [NSMutableData dataWithBytes:byteData length:length];
[data replaceBytesInRange:NSMakeRange(length*rangeToCut, length-(length*rangeToCut)) withBytes:NULL length:0];
[data writeToFile:[self.recordedFileURL path] atomically:YES];
it is erasing correctly but after that when i resume my audio ad append resumed part to old part like following:
NSMutableData *part2=[NSMutableData dataWithContentsOfURL:self.soundFileURL options:NSDataReadingUncached error:nil];
NSFileHandle *file = [NSFileHandle fileHandleForWritingToURL:oldURL error:nil];
if (file) {
[file seekToEndOfFile];
[file writeData:part2];
}
then audio files are appended successfully but resumed part of audio has too much disturbance not able to listen that part.
Please help me what is going wrong here.
Is your sample size 16 bits or more? If you cut the audio in the middle of a sample the rest of the stream will be just noise. You need to be sure of length*rangeToCut being a multiple of the sample size.
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
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. =)
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.
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.
hope this will help
- (unsigned long long) fastFolderSizeAtFSRef:(NSString *)theFilePath
{
unsigned long long totalSize = 0;
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isdirectory;
NSError *error;
if ([fileManager fileExistsAtPath:theFilePath])
{
NSMutableArray * directoryContents = [[fileManager contentsOfDirectoryAtPath:theFilePath error:&error] mutableCopy];
for (NSString *fileName in directoryContents)
{
if (([fileName rangeOfString:#".DS_Store"].location != NSNotFound) )
continue;
NSString *path = [theFilePath stringByAppendingPathComponent:fileName];
if([fileManager fileExistsAtPath:path isDirectory:&isdirectory] && isdirectory )
{
totalSize = totalSize + [self fastFolderSizeAtFSRef:path];
}
else
{
unsigned long long fileSize = [[fileManager attributesOfItemAtPath:path error:&error] fileSize];
totalSize = totalSize + fileSize;
}
}
}
return totalSize;
}
This is typically how it is done. 2 possibilities:
Check your byte -> megabyte conversion routines. Also, do you want megabytes or mebibytes? (It probably depends on what you're comparing it to.)
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).
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.
This code is as extension(category) to the NSFileManager class. It sums the sizes of all folder content.
Note that error treatment could be enhanced.
#interface NSFileManager(Util)
- (NSNumber *)sizeForFolderAtPath:(NSString *) source error:(NSError **)error;
#end
#implementation NSFileManager(Util)
- (NSNumber *)sizeForFolderAtPath:(NSString *) source error:(NSError **)error
{
NSArray * contents;
unsigned long long size = 0;
NSEnumerator * enumerator;
NSString * path;
BOOL isDirectory;
// Determine Paths to Add
if ([self fileExistsAtPath:source isDirectory:&isDirectory] && isDirectory)
{
contents = [self subpathsAtPath:source];
}
else
{
contents = [NSArray array];
}
// Add Size Of All Paths
enumerator = [contents objectEnumerator];
while (path = [enumerator nextObject])
{
NSDictionary * fattrs = [self attributesOfItemAtPath: [ source stringByAppendingPathComponent:path ] error:error];
size += [[fattrs objectForKey:NSFileSize] unsignedLongLongValue];
}
// Return Total Size in Bytes
return [ NSNumber numberWithUnsignedLongLong:size];
}
#end