IPhoneアプリでドキュメント内の画像をキャッシュするフォルダーを作成しています。このフォルダーのサイズを1MBに抑えたいので、フォルダーのサイズをバイト単位で確認する必要があります。
ファイルのサイズ を計算するコードがありますが、フォルダーのサイズが必要です。
これを行う最良の方法は何でしょうか?
他のすべての答えはオフです:)
すべてが非常に類似しているが、場合によっては非常に不正確な結果をもたらす多くの回答があるように見えるので、この古い質問に2セントを追加したいと思います。
なぜフォルダのサイズとは何かを定義する必要がある理由を理解するために。私の理解では(おそらくOPの1つ)、そのコンテンツをすべて含むディレクトリがボリュームで使用するバイト数です。または、別の言い方をすれば:
ディレクトリが完全に削除される場合に使用可能になるスペースです。
私はこの定義が質問を解釈する唯一の有効な方法ではないことを知っていますが、ほとんどのユースケースが要約するものだと思います。
既存の回答はすべて非常に単純なアプローチを取ります。ディレクトリの内容を走査して、(通常の)ファイルのサイズを合計します。これは、いくつかの微妙な点を考慮していません。
これらのすべての理由により、既存の回答は不正確な結果を生み出します。そこで、問題を解決するために、NSFileManager
(長さのためにgithubのコード: Swift 4 、 Objective C )でこの拡張機能を提案しています。また、特に多くのファイルを含むディレクトリでは、かなり高速です。
ソリューションの中核は、NSURL
のNSURLTotalFileAllocatedSizeKey
またはNSURLFileAllocatedSizeKey
プロパティを使用してファイルサイズを取得することです。
また、 シンプルなiOSテストプロジェクト を設定し、ソリューション間の違いを示しました。いくつかのシナリオで結果がどれほど間違っているかを示しています。
テストでは、100個の小さなファイル(0〜800バイトの範囲)を含むディレクトリを作成します。 folderSize:
他の回答からコピーされたメソッドは合計21 kBを計算しますが、私のallocatedSize
メソッドは401 kBを生成します。
テストディレクトリを削除する前と削除した後のボリュームで使用可能なバイトの差を計算することで、allocatedSize
の結果が正しい値に近いことを確認しました。私のテストでは、違いは常にallocatedSize
の結果と正確に等しくなりました。
Rob Napierのコメントを参照して、まだ改善の余地があることを理解してください。
しかし、もう1つの利点があります。1000ファイルのディレクトリのサイズを計算するとき、iPhone 6ではfolderSize:
メソッドは約250ミリ秒かかりますが、allocatedSize
は35ミリ秒で同じ階層を走査します。
これは、おそらくNSFileManager
のnew(ish)enumeratorAtURL:includingPropertiesForKeys:options:errorHandler:
階層を横断するAPI。このメソッドを使用すると、反復されるアイテムのプリフェッチプロパティを指定して、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)
アレックス、あなたは多くのことを助けて、今ではトリックを行う次の関数を書いています...
- (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は2つの数値を返します。 1つはディスク上のサイズで、もう1つは実際のバイト数です。
たとえば、フォルダの1つでこのコードを実行すると、「fileSize」が130398のコードに戻ります。Finderで確認すると、ディスク上のサイズは201KB(130,398バイト)と表示されます。
実際のサイズとしてここ(201KBまたは130,398バイト)で何をすればよいか少しわかりません。今のところ、私は安全な側に行き、これが正確に何を意味するかを知るまで私の制限を半分に減らします...
誰かがこれらの異なる番号にさらに情報を追加できるなら、私はそれを感謝します。
乾杯、
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;
}
これは、フォルダとファイルを取得する方法ですsize
in[〜#〜] mb [〜#〜]、[〜#〜] kb [〜#〜]および[〜# 〜] gb [〜#〜]---
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 _ {
return nil
}
}
次のようなものは、あなたが始めるのに役立つはずです。変更する必要があります_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;
}
このコードを使用して、2つのディレクトリのディレクトリサイズを取得しました。1つのディレクトリが存在しない場合、ゼロ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];
}
以下にSwift 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))
以下はSwift 3 @vitalii拡張に基づくFileManager拡張に相当するものです:
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
}}
列挙ブロックを使用する更新されたメソッド
ファイルのみでフォルダーサイズを計算
- (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;
}
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;
}
迅速な実装
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
}
使用する前に最初の回答の実装を少しクリーンアップしたため、非推奨の警告がスローされなくなり、高速列挙が使用されなくなりました。
/**
* 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;
}
任意のファイルのサイズを取得する場合は、そのファイルのパスのみを渡す必要があるメソッドがあります。
- (unsigned long long int) fileSizeAt:(NSString *)path {
NSFileManager *_manager = [NSFileManager defaultManager];
return [[_manager fileAttributesAtPath:path traverseLink:YES] fileSize];
}
これが誰かに役立つかどうかはわかりませんが、私が見つけたもののいくつか(上記の@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]
含まれるすべてのファイルのサイズの再帰的な合計ではなく、ディレクトリ情報のサイズを返します(FinderのláI-I)。
簡単な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")