我正在创建一个文件夹,以使用我的iPhone App在“文档”中缓存图像。我希望能够将此文件夹的大小减小到1MB,因此我需要检查文件夹的大小(以字节为单位)。
最好的方法是什么?
Answers:
所有其他答案都关闭了:)
我想在这个老问题上加上两分钱,因为似乎有很多答案都非常相似,但是在某些情况下得出的结果非常不准确。
要了解为什么我们首先必须定义文件夹的大小。以我的理解(可能是OP之一),它是目录(包括其所有内容)在卷上使用的字节数。或者,换一种说法:
如果目录将被完全删除,则该空间变为可用。
我知道这个定义不是解释问题的唯一有效方法,但我确实认为这是大多数用例的根源。
现有的答案都采用非常简单的方法:遍历目录内容,增加(常规)文件的大小。这并没有考虑到一些微妙之处。
所有这些原因使现有的答案产生不精确的结果。因此,我提议在此扩展名NSFileManager
(由于长度而在github上的代码:Swift 4,Objective C)来解决这个问题。它也相当快,特别是对于包含很多文件的目录。
该解决方案的核心是使用NSURL
的NSURLTotalFileAllocatedSizeKey
或NSURLFileAllocatedSizeKey
性的判定来检索文件的大小。
我还建立了一个简单的iOS测试项目,演示了解决方案之间的差异。它显示了在某些情况下结果可能是完全错误的。
在测试中,我创建一个包含100个小文件的目录(范围从0到800字节)。folderSize:
从其他答案中复制的方法总共计算出21 kB,而我的allocatedSize
方法得出401 kB。
allocatedSize
通过计算删除测试目录之前和之后卷上可用字节的差异,我确保的结果更接近正确的值。在我的测试中,差异始终等于的结果allocatedSize
。
请参阅Rob Napier的评论,以了解仍有改进的空间。
但是还有另一个优点:在计算包含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)
NSFileManager.defaultManager().nr_getAllocatedSize(UnsafeMutablePointer<UInt64>, ofDirectoryAtURL: NSURL!)
。但是我不确定应该向size
参数传递什么。我浏览了您的演示项目,但仍然有些困惑。
var size = UInt64(0); nr_getAllocatedSize(&size, ofDirectoryAtURL: someURL)
为亚历克斯欢呼,您提供了很多帮助,现在已经编写了以下功能,可以发挥作用...
- (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中签入时,它说磁盘上的大小为201KB(130,398字节)。
有点不确定此处的实际大小(201KB或130,398字节)。现在,我会安全地将限制减少一半,直到我知道这到底意味着什么...
如果有人可以向这些不同的号码添加更多信息,我将不胜感激。
干杯,
这是如何获得文件夹和文件size
的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 {
print(error)
}
return nil
}
在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;
}
类似以下内容应有助于您入门。不过,您需要修改_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个目录的目录大小,如果一个目录不存在,它将显示零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];
}
这是一个使用扩展的快速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))
使用枚举块更新了方法
仅使用文件计算文件夹大小
- (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;
}
这是基于@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
}}
我认为使用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;
}
如果我们想获取任何文件的大小,则这里是一个方法,我们只需要传递该文件的路径即可。
- (unsigned long long int) fileSizeAt:(NSString *)path {
NSFileManager *_manager = [NSFileManager defaultManager];
return [[_manager fileAttributesAtPath:path traverseLink:YES] 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;
}
快速实施
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
}
不确定这是否对任何人有帮助,但是我想介绍一下我的一些发现(一些受上述@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")