Je crée un dossier pour mettre en cache les images dans Documents avec mon application iPhone. Je veux pouvoir réduire la taille de ce dossier à 1 Mo. Je dois donc vérifier la taille en octets de mon dossier.
J'ai du code pour calculer la taille du fichier , mais j'ai besoin de la taille du dossier.
Quelle serait la meilleure façon de faire cela?
Toutes les autres réponses sont désactivées :)
Je voudrais ajouter mes deux cents à cette vieille question car il semble y avoir beaucoup de réponses qui sont toutes très similaires mais donnent des résultats qui sont dans certains cas très imprécis.
Pour comprendre pourquoi nous devons d'abord définir quelle est la taille d'un dossier. À ma connaissance (et probablement celle de l'OP), c'est la quantité d'octets que le répertoire, y compris tout son contenu, utilise sur le volume. Ou, autrement dit:
C'est l'espace qui devient disponible si le répertoire est complètement supprimé.
Je suis conscient que cette définition n'est pas le seul moyen valable d'interpréter la question, mais je pense que c'est à cela que se résument la plupart des cas d'utilisation.
Les réponses existantes adoptent toutes une approche très simple: parcourir le contenu du répertoire, en additionnant la taille des fichiers (normaux). Cela ne prend pas en compte quelques subtilités.
Toutes ces raisons font que les réponses existantes produisent des résultats imprécis. Je propose donc cette extension sur NSFileManager
(code sur github en raison de la longueur: Swift 4 , Objective C ) pour remédier au problème. C'est aussi un peu plus rapide, surtout avec les répertoires contenant beaucoup de fichiers.
Le cœur de la solution consiste à utiliser les propriétés NSURL
NSURLTotalFileAllocatedSizeKey
ou NSURLFileAllocatedSizeKey
pour récupérer la taille des fichiers.
J'ai également mis en place n simple projet de test iOS , démontrant les différences entre les solutions. Il montre à quel point les résultats peuvent être totalement erronés dans certains scénarios.
Dans le test, je crée un répertoire contenant 100 petits fichiers (allant de 0 à 800 octets). La méthode folderSize:
Copiée à partir d'une autre réponse calcule un total de 21 Ko alors que ma méthode allocatedSize
donne 401 Ko.
Je me suis assuré que les résultats de allocatedSize
sont plus proches de la valeur correcte en calculant la différence des octets disponibles sur le volume avant et après la suppression du répertoire de test. Dans mes tests, la différence était toujours exactement égale au résultat de allocatedSize
.
Veuillez lire le commentaire de Rob Napier pour comprendre qu'il y a encore place à amélioration.
Mais il y a un autre avantage: lors du calcul de la taille d'un répertoire de 1000 fichiers, sur mon iPhone 6, la méthode folderSize:
Prend environ 250 ms tandis que allocatedSize
parcourt la même hiérarchie en 35 ms.
Cela est probablement dû à l'utilisation de la nouvelle API (ish) enumeratorAtURL:includingPropertiesForKeys:options:errorHandler:
De NSFileManager
pour parcourir la hiérarchie. Cette méthode vous permet de spécifier des propriétés prérécupérées pour les éléments à itérer, ce qui entraîne moins d'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)
Bravo pour cet Alex, vous avez beaucoup aidé, avez maintenant écrit la fonction suivante qui fait l'affaire ...
- (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;
}
Il arrive avec le nombre exact d'octets comme le fait le Finder.
En passant, Finder renvoie deux nombres. L'un est la taille du disque et l'autre le nombre réel d'octets.
Par exemple, lorsque j'exécute ce code sur l'un de mes dossiers, il revient dans le code avec une "taille de fichier" de 130398. Lorsque j'archive le Finder, il indique que la taille est de 201 Ko sur le disque (130 398 octets).
Je ne sais pas trop quoi choisir ici (201 Ko ou 130 398 octets) comme taille réelle. Pour l'instant, je vais aller du bon côté et réduire ma limite de moitié jusqu'à ce que je sache ce que cela signifie exactement ...
Si quelqu'un peut ajouter plus d'informations à ces différents chiffres, je l'apprécierais.
À votre santé,
Dans iOS 5, la méthode -filesAttributesAtPath:
est obsolète. Voici la version du premier code posté avec la nouvelle méthode:
- (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;
}
Voici comment obtenir le dossier et le fichier size
dans [~ # ~] mb [~ # ~] = , KO et GB ---
1. Taille du dossier -
-(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;
}
Remarque: Dans le cas de sous-dossiers, veuillez utiliser subpathsOfDirectoryAtPath:
au lieu de contentsOfDirectoryAtPath:
2. Taille du fichier -
-(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. Taille du dossier -
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. Taille du fichier -
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
}
}
Quelque chose comme ce qui suit devrait vous aider à démarrer. Vous devrez modifier _documentsDirectory
dans votre dossier spécifique, cependant:
- (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;
}
J'ai utilisé ce code pour obtenir la taille du répertoire de 2 répertoires, si un répertoire n'existait pas, il afficherait zéro Ko. Sinon, la seconde moitié du code affichera la taille du dossier avec les Ko, Mo, Go, respectivement, et l'affichera également dans un format propre: 10.02 MB
.
Essayez ceci quelque chose comme ceci:
- (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];
}
Voici une réponse Swift 2.1/2.2 en utilisant des extensions et en s'appuyant sur la réponse de 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
}
}
Exemple d'utilisation:
let fileManager = NSFileManager.defaultManager()
let documentsDirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let dirSize: String = fileManager.format(fileManager.folderSizeAtPath(documentsDirPath))
Voici l'équivalent Swift 3 d'une extension FileManager basée sur l'extension @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
}}
Méthode mise à jour utilisant un bloc d'énumération
Calculer la taille du dossier avec uniquement des fichiers
- (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;
}
Calculer la taille du dossier avec les autres sous-répertoires du dossier
NSArray *folderContents = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
Obtenir la taille du fichier
- (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;
}
Je pense que l'utilisation de la méthode Unix C est meilleure pour les performances.
+ (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;
}
Mise en œuvre rapide
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
}
J'ai nettoyé un peu l'implémentation de la première réponse avant de l'utiliser, donc elle ne lance plus d'avertissements obsolètes + en utilisant une énumération rapide.
/**
* 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;
}
si nous voulons obtenir la taille d'un fichier, voici une méthode, où nous n'avons besoin que de passer le chemin de ce fichier.
- (unsigned long long int) fileSizeAt:(NSString *)path {
NSFileManager *_manager = [NSFileManager defaultManager];
return [[_manager fileAttributesAtPath:path traverseLink:YES] fileSize];
}
Je ne sais pas si cela aide quelqu'un, mais je voulais raconter certaines de mes conclusions (certaines inspirées par le commentaire de @ zneak ci-dessus).
Je n'ai trouvé aucun raccourci en utilisant NSDirectoryEnumerator
pour éviter d'énumérer les fichiers afin d'obtenir la taille totale contenue d'un répertoire.
Pour mes tests, en utilisant -[NSFileManager subpathsOfDirectoryAtPath:path error:nil]
était plus rapide que l'utilisation de -[NSFileManager enumeratorAtPath:path]
. Cela me semble être un compromis temps/espace classique, comme subPaths...
crée un NSArray sur lequel il itère ensuite, où enumerator...
peut-être pas.
Quelques informations sur # 1. En supposant:
NSFileManager *fileMan = [NSFileManager defaultManager];
NSString *dirPath = @"/"; // references some directory
Alors
[fileMan enumeratorAtPath:dirPath] fileAttributes]
renvoie nil
. L'accesseur d'attribut correct est directoryAttributes
, mais
[fileMan enumeratorAtPath:dirPath] directoryAttributes] fileSize]
renvoie la taille des informations du répertoire, et non la somme récursive des tailles de tous les fichiers contenus (un lá ⌘-I dans le Finder).
J'ai créé une simple extension 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
}
}
Vous pouvez obtenir la taille du fichier:
NSFileManager.defaultManager().fileSizeAtPath("file path")
et la taille du dossier:
NSFileManager.defaultManager().folderSizeAtPath("folder path")