Last active
August 27, 2024 20:29
-
-
Save tmspzz/a75f589e6bd86aa2121618155cbdf827 to your computer and use it in GitHub Desktop.
A method to calculate the accumulated size of a directory on the volume in bytes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public extension FileManager { | |
/// This method calculates the accumulated size of a directory on the volume in bytes. | |
/// | |
/// As there's no simple way to get this information from the file system it has to crawl the entire hierarchy, | |
/// accumulating the overall sum on the way. The resulting value is roughly equivalent with the amount of bytes | |
/// that would become available on the volume if the directory would be deleted. | |
/// | |
/// - note: There are a couple of oddities that are not taken into account (like symbolic links, meta data of | |
/// directories, hard links, ...). | |
public func allocatedSizeOfDirectory(atUrl url: URL) throws -> UInt64 { | |
// We'll sum up content size here: | |
var accumulatedSize: UInt64 = 0 | |
// prefetching some properties during traversal will speed up things a bit. | |
let prefetchedProperties = [ | |
URLResourceKey.isRegularFileKey | |
, URLResourceKey.fileAllocatedSizeKey | |
, URLResourceKey.totalFileAllocatedSizeKey | |
] | |
// The error handler simply signals errors to outside code. | |
var errorDidOccur: Error? | |
let errorHandler: (URL, Error) -> Bool = { _, error in | |
errorDidOccur = error | |
return false | |
} | |
// We have to enumerate all directory contents, including subdirectories. | |
let enumerator = self.enumerator(at: url, | |
includingPropertiesForKeys: prefetchedProperties, | |
options: FileManager.DirectoryEnumerationOptions.init(rawValue: 0), | |
errorHandler: errorHandler) | |
// Start the traversal: | |
while let contentURL = (enumerator?.nextObject() as? URL) { | |
// Bail out on errors from the errorHandler. | |
if let error = errorDidOccur { throw error } | |
// Get the type of this item, making sure we only sum up sizes of regular files. | |
let resourceValues = try contentItemURL.resourceValues(forKeys: [.isRegularFileKey, .totalFileAllocatedSizeKey, .fileAllocatedSizeKey]) | |
guard resourceValues.isRegularFile ?? false else { | |
continue | |
} | |
// To get the file's size we first try the most comprehensive value in terms of what the file may use on disk. | |
// This includes metadata, compression (on file system level) and block size. | |
var fileSize = resourceValues.fileSize | |
// In case the value is unavailable we use the fallback value (excluding meta data and compression) | |
// This value should always be available. | |
fileSize = fileSize ?? resourceValues.totalFileAllocatedSize | |
// We're good, add up the value. | |
accumulatedSize += UInt64(fileSize ?? 0) | |
} | |
// Bail out on errors from the errorHandler. | |
if let error = errorDidOccur { throw error } | |
// We finally got it. | |
return accumulatedSize | |
} | |
} |
Also, line 46 should be contentURL
not contentItemURL
A better version is available at https://gist.github.com/NikolaiRuhe/408cefb953c4bea15506a3f80a3e5b96
A simpler version without error handling or edge case handling.
/// Calculates the size of a directory and all its contents.
/// - Parameter url: The directory's URL.
/// - Returns: The total size in bytes.
func sizeOfDirectory(at url: URL) -> Int? {
guard let enumerator = enumerator(at: url, includingPropertiesForKeys: [.fileSizeKey]) else { return nil }
var size = 0
for case let fileURL as URL in enumerator {
guard let fileSize = try? fileURL.resourceValues(forKeys: [.fileSizeKey]).fileSize else {
continue
}
size += fileSize
}
return size
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I think you swapped the keys by mistake.
On Line 54 it should be
var fileSize = resourceValues.totalFileAllocatedSize
and therefore on line 58
fileSize = fileSize ?? resourceValues.fileAllocatedSize
.