Come posso calcolare la dimensione di una cartella?

Sto creando una cartella per memorizzare immagini all’interno di Documenti con la mia app per iPhone. Voglio essere in grado di mantenere la dimensione di questa cartella fino a 1 MB, quindi ho bisogno di controllare la dimensione in byte della mia cartella.

Ho il codice per calcolare la dimensione del file , ma ho bisogno della dimensione della cartella.

Quale sarebbe il modo migliore per farlo?

tl; dr

Tutte le altre risposte sono off 🙂

Problema

Vorrei aggiungere i miei due centesimi a questa vecchia domanda in quanto sembrano esserci molte risposte che sono tutte molto simili ma danno risultati che in alcuni casi sono molto imprecisi.

Per capire perché dobbiamo prima definire qual è la dimensione di una cartella . Nella mia comprensione (e probabilmente quella dell’OP) è la quantità di byte che la directory che include tutti i suoi contenuti utilizza sul volume. Oppure, mettilo in un altro modo:

È lo spazio che diventa disponibile se la directory viene completamente rimossa.

Sono consapevole del fatto che questa definizione non è l’unico modo valido per interpretare la domanda, ma penso che sia ciò a cui si riconducono i casi più utili.

Errore

Le risposte esistenti adottano un approccio molto semplice: attraversano i contenuti della directory, sumndo le dimensioni dei file (regolari). Questo non tiene conto di un paio di sottigliezze.

  • Lo spazio utilizzato sul volume aumenta in blocchi , non in byte. Anche un file di un byte utilizza almeno un blocco.
  • I file portano in giro meta dati (come qualsiasi numero di attributi estesi). Questi dati devono andare da qualche parte.
  • HFS implementa la compressione del file system per archiviare effettivamente il file utilizzando meno byte e quindi la sua lunghezza reale.

Soluzione

Tutti questi motivi fanno sì che le risposte esistenti producano risultati non precisi. Quindi sto proponendo questa estensione su NSFileManager (codice su github a causa della lunghezza: Swift 4 , Objective C ) per rimediare al problema. È anche un po ‘più veloce, specialmente con le directory che contengono molti file.

Il nucleo della soluzione consiste nell’utilizzare le NSURL NSURLTotalFileAllocatedSizeKey o NSURLFileAllocatedSizeKey per recuperare le dimensioni dei file.

Test

Ho anche creato un semplice progetto di test iOS , dimostrando le differenze tra le soluzioni. Mostra quanto siano errati i risultati in alcuni scenari.

Nel test creo una directory contenente 100 file di piccole dimensioni (da 0 a 800 byte). La folderSize: metodo copiato da un’altra risposta calcola un totale di 21 kB mentre il metodo folderSize: restituisce 401 kB.

Prova

Mi sono assicurato che i risultati di allocatedSize fossero più vicini al valore corretto calcolando la differenza dei byte disponibili sul volume prima e dopo aver eliminato la directory di test. Nei miei test la differenza era sempre esattamente uguale al risultato di allocatedSize .

Si prega di consultare il commento di Rob Napier per capire che c’è ancora spazio per miglioramenti.

Prestazione

Ma c’è un altro vantaggio: quando si calcola la dimensione di una directory con 1000 file, sul mio iPhone 6 il folderSize: metodo impiega circa 250 ms mentre allocatedSize attraversa la stessa gerarchia in 35 ms.

Ciò è probabilmente dovuto all’utilizzo del nuovo enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: (ish) di enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: API per attraversare la gerarchia. Questo metodo consente di specificare le proprietà di precaricamento per gli elementi da iterare, con una conseguente riduzione di io.

risultati

 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) 

Saluti per questo Alex, hai aiutato molto, ora hai scritto la seguente funzione che fa il trucco …

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

Sta arrivando con il numero esatto di byte come fa il Finder.

Per inciso, Finder restituisce due numeri. Uno è la dimensione sul disco e l’altro è il numero effettivo di byte.

Ad esempio, quando eseguo questo codice su una delle mie cartelle, ritorna nel codice con un ‘fileSize’ di 130398. Quando eseguo il check-in Finder, si dice che la dimensione è di 201 KB sul disco (130.398 byte).

Sono un po ‘insicuro su cosa andare qui (201 KB o 130.398 byte) come dimensione effettiva. Per ora, vado dalla parte della sicurezza e metto a metà il limite fino a scoprire cosa significa esattamente …

Se qualcuno può aggiungere ulteriori informazioni a questi numeri diversi, lo apprezzerei.

Saluti,

In iOS 5 il metodo -filesAttributesAtPath: è deprecato. Ecco la versione del primo codice pubblicato con il nuovo metodo:

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

Ecco come ottenere le size cartelle e file in MB , KB e GB

1. Dimensioni cartella –

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

Nota: in caso di sottocartelle, utilizzare subpathsOfDirectoryAtPath: invece di contentsOfDirectoryAtPath:

2. Dimensione del file –

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

———- Swift 4.0 ———-

1. Dimensioni cartella –

 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. Dimensione del file –

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

Qualcosa come il seguente dovrebbe aiutarti a iniziare. Dovrai modificare _documentsDirectory nella tua cartella specifica, tuttavia:

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

Ho usato questo codice per ottenere la dimensione della directory di 2 directory, se una directory non esistesse, mostrerebbe Zero KB. In caso contrario, la seconda metà del codice visualizzerà la dimensione della cartella insieme a KB, MB, GB, rispettivamente, e la visualizzerà anche in un formato pulito: 10.02 MB .

Prova questo qualcosa del genere:

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

Ecco una rapida risposta 2.1 / 2.2 usando le estensioni e costruendo la risposta di 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 } } 

Esempio di utilizzo:

 let fileManager = NSFileManager.defaultManager() let documentsDirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] let dirSize: String = fileManager.format(fileManager.folderSizeAtPath(documentsDirPath)) 

Metodo aggiornato utilizzando il blocco di enumerazione

Calcola dimensioni della cartella con solo file

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

Calcola dimensioni della cartella con altre sottodirectory nella cartella

  NSArray *folderContents = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil]; 

Ottieni dimensioni file

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

Ecco l’equivalente di Swift 3 di un’estensione FileManager basata sull’estensione @vitalii:

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

Penso che usare il metodo Unix C sia migliore per le prestazioni.

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

se vogliamo ottenere la dimensione di qualsiasi file, ecco un metodo, in cui dobbiamo solo passare il percorso di quel file.

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

Ho ripulito un po ‘l’implementazione della prima risposta prima di usarla, quindi non getta più avvertimenti deprecati + usando l’enumerazione veloce.

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

Implementazione rapida

 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 } 

Non sono sicuro se questo aiuti qualcuno, ma volevo mettere in relazione alcuni dei miei risultati (alcuni ispirati al commento di @ zneak sopra).

  1. Non sono riuscito a trovare scorciatoie utilizzando NSDirectoryEnumerator per evitare di enumerare i file per ottenere la dimensione totale contenuta di una directory.

  2. Per i miei test, utilizzando -[NSFileManager subpathsOfDirectoryAtPath:path error:nil] stato più rapido dell’utilizzo -[NSFileManager enumeratorAtPath:path] . Questo mi sembra possa essere un classico compromesso tempo / spazio, in quanto subPaths... crea un NSArray su cui poi itera, dove l’ enumerator... potrebbe non farlo.

Qualche sfondo sul # 1. assumendo:

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

Poi

 [fileMan enumeratorAtPath:dirPath] fileAttributes] 

ritorna nil . L’attributo di accesso corretto è directoryAttributes , ma

 [fileMan enumeratorAtPath:dirPath] directoryAttributes] fileSize] 

restituisce la dimensione delle informazioni della directory, non la sum ricorsiva delle dimensioni di tutti i file contenuti (a lá ⌘-I in Finder).

Ho creato una semplice estensione 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 } } 

Puoi ottenere la dimensione del file:

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

e la dimensione della cartella:

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