Как я могу рассчитать размер папки?
Я создаю папку для кэширования изображений внутри документов с помощью моего приложения для iPhone. Я хочу сохранить размер этой папки до 1 МБ, поэтому мне нужно проверить размер в байтах моей папки.
У меня есть код для расчета размера файла, но мне нужен размер папки.
Каков был бы лучший способ сделать это?
Ответы
Ответ 1
TL;DR
Все остальные ответы отключены:)
Проблема
Я хотел бы добавить свои два цента к этому старому вопросу, так как, кажется, есть много ответов, которые все очень похожи, но дают результаты, которые в некоторых случаях очень неточны.
Чтобы понять, почему мы сначала должны определить, каков размер папки. В моем понимании (и, вероятно, в одном из OP) это количество байтов, которое каталог, включая все его содержимое, использует на томе. Или, по-другому:
Это пространство становится доступным, если каталог будет полностью удален.
Мне известно, что это определение - не единственный действительный способ интерпретации вопроса, но я думаю, что именно к этому относится большинство случаев использования.
Ошибка
Все существующие ответы используют очень простой подход: просмотрите содержимое каталога, сложив размеры (обычных) файлов. Это не учитывает пару тонкостей.
- Пространство, используемое для увеличения громкости в блоках blocks, а не в байтах. Даже однобайтовый файл использует хотя бы один блок.
- Файлы содержат метаданные meta data (как любое количество расширенных атрибутов). Эти данные должны куда-то идти.
- HFS развертывает файловую систему , сжатие, чтобы фактически сохранить файл, используя меньше байтов, чем его реальная длина.
Решение
Все эти причины заставляют существующие ответы давать неточные результаты. Поэтому я предлагаю это расширение на NSFileManager
(код на github из-за длины: Swift 4, Objective C) для решения проблемы. Это также немного быстрее, особенно с каталогами, содержащими много файлов.
Суть решения заключается в использовании свойств NSURL
NSURLTotalFileAllocatedSizeKey
или NSURLFileAllocatedSizeKey
для получения размеров файлов.
Тест
Я также создал простой тестовый проект для iOS, демонстрирующий различия между решениями. Он показывает, насколько ошибочными могут быть результаты в некоторых сценариях.
В тесте я создаю каталог, содержащий 100 маленьких файлов (от 0 до 800 байт). Метод folderSize:
, скопированный из какого-либо другого ответа, вычисляет в общей сложности 21 кБ, в то время как мой метод allocatedSize
дает 401 кБ.
Доказательство
Я убедился, что результаты allocatedSize
ближе к правильному значению, рассчитав разницу доступных байтов в томе до и после удаления каталога тестов. В моих тестах разница всегда была точно равна результату allocatedSize
.
Пожалуйста, посмотрите комментарий Роба Нейпира, чтобы понять, что еще есть возможности для совершенствования.
Производительность
Но есть и другое преимущество: при расчете размера каталога с 1000 файлами на моем iPhone 6 метод folderSize:
занимает около 250 мс, в то время как allocatedSize
проходит ту же иерархию за 35 мс.
Вероятно, это связано с использованием NSFileManager
new (ish) API enumeratorAtURL:includingPropertiesForKeys:options:errorHandler:
для обхода иерархии. Этот метод позволяет указать предварительно выбранные свойства для элементов, подлежащих итерации, что приводит к уменьшению количества операций ввода-вывода.
Результаты
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)
Ответ 2
Приветствия за то, что Алекс, ты много помог, теперь написал следующую функцию, которая делает трюк...
- (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;
}
Это число с точным количеством байтов, как это делает Finder.
В качестве альтернативы Finder возвращает два числа. Один - это размер на диске, а другой - фактическое количество байтов.
Например, когда я запускаю этот код в одной из моих папок, он возвращается в код с файлом размером $130398. Когда я проверяю Finder, он говорит, что размер составляет 201 КБ на диске (130,398 байт).
Немного не уверен, что с этим делать (201 КБ или 130,398 байт) в качестве фактического размера. Пока я пойду на безопасную сторону и сокращу свой лимит пополам, пока не узнаю, что это значит...
Если кто-нибудь может добавить дополнительную информацию к этим разным числам, я был бы признателен.
Приветствия,
Ответ 3
Это как получить папку и файл size
в МБ, КБ и ГБ ---
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;
}
---------- Swift 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
}
Ответ 4
В 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;
}
Ответ 5
Что-то вроде следующего должно помочь вам начать. Вам нужно будет изменить _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;
}
Ответ 6
Я использовал этот код, чтобы получить размер каталога из 2 каталогов, если один каталог не существует, он будет показывать Zero 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];
}
Ответ 7
Здесь быстрый ответ 2.1/2.2 с использованием расширений и построение ответа 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
}
}
Пример использования:
let fileManager = NSFileManager.defaultManager()
let documentsDirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let dirSize: String = fileManager.format(fileManager.folderSizeAtPath(documentsDirPath))
Ответ 8
Обновленный метод с использованием блока перечисления
Рассчитать размер папки только с файлами
- (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;
}
Ответ 9
Вот эквивалент Swift 3 расширения FileManager, основанный на расширении @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
}}
Ответ 10
Я думаю, что использовать метод Unix 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;
}
Ответ 11
если мы хотим получить размер любого файла, тогда вот метод, где нам нужно только пройти путь к этому файлу.
- (unsigned long long int) fileSizeAt:(NSString *)path {
NSFileManager *_manager = [NSFileManager defaultManager];
return [[_manager fileAttributesAtPath:path traverseLink:YES] fileSize];
}
Ответ 12
Я сначала очистил реализацию первого ответа до его использования, поэтому он больше не выдает устаревшие предупреждения +, используя быстрое перечисление.
/**
* 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;
}
Ответ 13
Быстрая реализация
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
}
Ответ 14
Не уверен, что это кому-то помогает, но я хотел бы рассказать о некоторых моих выводах (некоторые из них были вдохновлены комментарием @zneak выше).
-
Я не мог найти ярлыки с помощью NSDirectoryEnumerator
, чтобы избежать перечисления через файлы, чтобы получить общий размер содержимого каталога.
-
Для моих тестов использование -[NSFileManager subpathsOfDirectoryAtPath:path error:nil]
было быстрее, чем использование -[NSFileManager enumeratorAtPath:path]
. Мне кажется, что это может быть классический компромисс между временем и пространством, поскольку subPaths...
создает NSArray, на котором он итерации, где enumerator...
может не быть.
Немного фона на # 1. Предполагая, что:
NSFileManager *fileMan = [NSFileManager defaultManager];
NSString *dirPath = @"/"; // references some directory
Тогда
[fileMan enumeratorAtPath:dirPath] fileAttributes]
возвращает nil
. Правильный атрибут атрибута directoryAttributes
, но
[fileMan enumeratorAtPath:dirPath] directoryAttributes] fileSize]
возвращает размер информации о каталоге, а не рекурсивную сумму размеров всех содержащихся файлов (a lá ⌘-I в Finder).
Ответ 15
Я создал простое расширение 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")