ios 如何计算文件夹的大小?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2188469/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I calculate the size of a folder?
提问by joseph_carney
I'm creating a folder to cache images inside Documents with my iPhone App. I want to be able to keep the size of this folder down to 1MB, so I need to to check the size in bytes of my folder.
我正在使用我的 iPhone 应用程序创建一个文件夹来缓存文档中的图像。我希望能够将此文件夹的大小保持在 1MB,因此我需要检查文件夹的大小(以字节为单位)。
I have code to calculate the size of file, but I need the size of the folder.
我有计算文件大小的代码,但我需要文件夹的大小。
What would be the best way to do this?
什么是最好的方法来做到这一点?
回答by Nikolai Ruhe
tl;dr
tl;博士
All the other answers are off :)
所有其他答案都关闭了:)
Problem
问题
I'd like to add my two cents to this old question as there seem to be many answers that are all very similar but yield results that are in some cases very unprecise.
我想在这个老问题上加上我的两分钱,因为似乎有很多答案都非常相似,但在某些情况下会产生非常不精确的结果。
To understand why we first have to define what the size of a folderis. In my understanding (and probably the one of the OP) it is the amount of bytes that the directory including all of its contents uses on the volume. Or, put in another way:
要了解为什么我们首先必须定义文件夹的大小。根据我的理解(可能也是 OP 之一),它是目录(包括其所有内容)在卷上使用的字节数。或者,换一种方式:
It is the space becoming available if the directory would be completely removed.
如果目录被完全删除,它就是可用的空间。
I'm aware that this definition is not the only valid way to interpret the question but I do think it's what most use cases boil down to.
我知道这个定义不是解释问题的唯一有效方法,但我确实认为这是大多数用例归结为。
Error
错误
The existing answers all take a very simple approach: Traverse the directory contents, adding up the sizes of (regular) files. This does not take a couple of subtleties into account.
现有的答案都采用了一种非常简单的方法:遍历目录内容,将(常规)文件的大小相加。这并没有考虑到一些微妙之处。
- The space used on the volume increments in blocks, not in bytes. Even a one byte file uses at least one block.
- Files carry around meta data(like any number of extended attributes). This data must go somewhere.
- HFS deploys file system compressionto actually store the file using less bytes then its real length.
- 卷上使用的空间以块为单位增加,而不是以字节为单位。即使是一个字节的文件也至少使用一个块。
- 文件携带元数据(如任意数量的扩展属性)。这些数据必须去某个地方。
- HFS 部署文件系统压缩以使用比实际长度更少的字节来实际存储文件。
Solution
解决方案
All of these reasons make the existing answers produce unprecise results. So I'm proposing this extension on NSFileManager
(code on github due to length: Swift 4, Objective C) to remedy the problem. It's also quite a bit faster, especially with directories containing a lot of files.
所有这些原因使现有答案产生不准确的结果。所以我在NSFileManager
(由于长度原因在 github上的代码:Swift 4,Objective C)上提出这个扩展来解决这个问题。它也快了很多,尤其是对于包含大量文件的目录。
The core of the solution is to use NSURL
's NSURLTotalFileAllocatedSizeKey
or NSURLFileAllocatedSizeKey
properies to retrieve file sizes.
该解决方案的核心是使用NSURL
'sNSURLTotalFileAllocatedSizeKey
或NSURLFileAllocatedSizeKey
属性来检索文件大小。
Test
测试
I've also set up a simple iOS test project, demonstrating the differences between the solutions. It shows how utterly wrong the results can be in some scenarios.
我还建立了一个简单的 iOS 测试项目,展示了解决方案之间的差异。它显示了在某些情况下结果可能是多么错误。
In the test I create a directory containing 100 small files (ranging from 0 to 800 bytes). The folderSize:
method copied from some other answer calculates a total of 21 kB while my allocatedSize
method yields 401 kB.
在测试中,我创建了一个包含 100 个小文件(范围从 0 到 800 字节)的目录。folderSize:
从其他一些答案复制的方法总共计算了 21 kB,而我的allocatedSize
方法产生了 401 kB。
Proof
证明
I made sure that the results of allocatedSize
are closer to the correct value by calculating the difference of the available bytes on the volume before and after deleting the test directory. In my tests the difference was always exactly equal to the result of allocatedSize
.
我allocatedSize
通过计算删除测试目录前后卷上可用字节的差异来确保结果更接近正确的值。在我的测试中,差异总是完全等于allocatedSize
.
Please see Rob Napier's comment to understand that there's still room for improvement.
请参阅 Rob Napier 的评论以了解仍有改进的空间。
Performance
表现
But there's another advantage: When calculating the size of a directory with 1000 files, on my iPhone 6 the folderSize:
method takes about 250 ms while allocatedSize
traverses the same hierarchy in 35 ms.
但还有另一个优点:在计算包含 1000 个文件的目录的大小时,在我的 iPhone 6 上,该folderSize:
方法需要大约 250 毫秒,而allocatedSize
在 35 毫秒内遍历相同的层次结构。
This is probably due to using NSFileManager
's new(ish) enumeratorAtURL:includingPropertiesForKeys:options:errorHandler:
API to traverse the hierachy. This method let's you specify prefetched properties for the items to be iterated, resulting in less io.
这可能是由于使用了NSFileManager
new(ish) enumeratorAtURL:includingPropertiesForKeys:options:errorHandler:
API 来遍历层次结构。此方法让您为要迭代的项目指定预取属性,从而减少 io。
Results
结果
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)
回答by joseph_carney
Cheers for that Alex, you helped a lot, have now written the following function which does the trick...
为那个亚历克斯干杯,你帮了很多忙,现在已经编写了以下函数来解决这个问题……
- (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;
}
It is coming up with the exact number of bytes as Finder does.
它与 Finder 一样提供确切的字节数。
As an aside, Finder returns two numbers. One is the size on the disk and the other is the actual number of bytes.
顺便说一句,Finder 返回两个数字。一个是磁盘上的大小,另一个是实际的字节数。
For example, when I run this code on one of my folders, it comes back in the code with a 'fileSize' of 130398. When I check in Finder, it says the size is 201KB on disk (130,398 bytes).
例如,当我在我的一个文件夹上运行此代码时,它返回代码中的“fileSize”为 130398。当我检查 Finder 时,它说磁盘上的大小为 201KB(130,398 字节)。
Am a little unsure of what to go with here (201KB or 130,398 bytes) as the actual size. For now, I'll go on the safe side and cut my limit in half until I find out what this means exactly...
我有点不确定这里的实际大小(201KB 或 130,398 字节)。现在,我会安全起见,将我的限制减半,直到我弄清楚这到底意味着什么......
If anyone can add any more information to these differing numbers I'd appreciate it.
如果有人可以为这些不同的数字添加更多信息,我将不胜感激。
Cheers,
干杯,
回答by TheTiger
This is how to get folder and file size
in MB, KBand GB---
这是如何获得文件夹和文件size
的MB,KB和GB---
1. Folder 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;
}
Note:In case of sub folders please use subpathsOfDirectoryAtPath:
instead of contentsOfDirectoryAtPath:
注意:如果是子文件夹,请使用subpathsOfDirectoryAtPath:
而不是contentsOfDirectoryAtPath:
2. File Size -
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 ----------
---------- 斯威夫特 4.0 ----------
1. Folder Size -
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. File Size -
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
}
回答by ndoc
In iOS 5 the method -filesAttributesAtPath:
is deprecated. Here is the version of the first code posted with the new method:
在 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;
}
回答by Alex Reynolds
Something like the following should help get you started. You'll need to modify _documentsDirectory
to your specific folder, though:
类似以下内容应该可以帮助您入门。不过,您需要修改_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;
}
回答by WrightsCS
I used this code to get the directory size of 2 directories, if one directory didnt exist, it would show Zero KB. Otherwise, the second half of the code will display the folder size along with the KB, MB, GB, respectively, and it will also display it in a clean format: 10.02 MB
.
我使用这段代码来获取 2 个目录的目录大小,如果一个目录不存在,它将显示零 KB。否则,代码的后半部分将分别显示文件夹大小以及 KB、MB、GB,并且还会以干净的格式显示:10.02 MB
.
Try this something like this:
试试这个:
- (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];
}
回答by Krtko
Here's a swift 2.1/2.2 answer using extensions and building off of Rok's answer:
这是使用扩展并建立在 Rok 答案基础上的 2.1/2.2 快速答案:
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
}
}
Usage example:
用法示例:
let fileManager = NSFileManager.defaultManager()
let documentsDirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let dirSize: String = fileManager.format(fileManager.folderSizeAtPath(documentsDirPath))
回答by Larry Mickie
Here is the Swift 3 equivalent of a FileManager extension based off of @vitalii extension:
这是基于 @vitalii 扩展的 FileManager 扩展的 Swift 3 等价物:
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
}}
回答by icodebuster
Updated Method using enumeration block
使用枚举块更新方法
Calculate Folder Size with only files
仅使用文件计算文件夹大小
- (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;
}
Calculate Folder Size with other sub directories in the folder
使用文件夹中的其他子目录计算文件夹大小
NSArray *folderContents = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil];
Get File Size
获取文件大小
- (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;
}
回答by ZYiOS
I think use Unix C method is better for performance.
我认为使用 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;
}