如何使用 Swift 在 OS X 上获取目录大小
How To Get Directory Size With Swift On OS X
我正在尝试使用 Swift 获取目录的大小及其在 OS X 上的内容。到目前为止,我只能获得目录本身的大小及其内容的 none 。对于我的大多数目录,它通常显示 6,148 字节的值,但它确实有所不同。
我尝试了下面文件中的 directorySize() 函数,但它也返回了 6,148 字节。
我尝试了这个问题的前 2 个答案,但不确定它需要将什么参数传递给 Swift 到 Objective-C 函数。我相信它需要一个指针(我是学习中的初学者)。
Calculate the size of a folder
而且我也无法从这里得到 Swift 答案以达到我的目的。
How to get the file size given a path?
我正在使用 Xcode 7.0 和 运行 OS X 10.10.5.
更新:Xcode 11.4.1 • Swift 5.2
extension URL {
/// check if the URL is a directory and if it is reachable
func isDirectoryAndReachable() throws -> Bool {
guard try resourceValues(forKeys: [.isDirectoryKey]).isDirectory == true else {
return false
}
return try checkResourceIsReachable()
}
/// returns total allocated size of a the directory including its subFolders or not
func directoryTotalAllocatedSize(includingSubfolders: Bool = false) throws -> Int? {
guard try isDirectoryAndReachable() else { return nil }
if includingSubfolders {
guard
let urls = FileManager.default.enumerator(at: self, includingPropertiesForKeys: nil)?.allObjects as? [URL] else { return nil }
return try urls.lazy.reduce(0) {
(try .resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize ?? 0) + [=10=]
}
}
return try FileManager.default.contentsOfDirectory(at: self, includingPropertiesForKeys: nil).lazy.reduce(0) {
(try .resourceValues(forKeys: [.totalFileAllocatedSizeKey])
.totalFileAllocatedSize ?? 0) + [=10=]
}
}
/// returns the directory total size on disk
func sizeOnDisk() throws -> String? {
guard let size = try directoryTotalAllocatedSize(includingSubfolders: true) else { return nil }
URL.byteCountFormatter.countStyle = .file
guard let byteCount = URL.byteCountFormatter.string(for: size) else { return nil}
return byteCount + " on disk"
}
private static let byteCountFormatter = ByteCountFormatter()
}
用法:
do {
let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
if let sizeOnDisk = try documentDirectory.sizeOnDisk() {
print("Size:", sizeOnDisk) // Size: 3.15 GB on disk
}
} catch {
print(error)
}
Swift 3 版本在这里:
func findSize(path: String) throws -> UInt64 {
let fullPath = (path as NSString).expandingTildeInPath
let fileAttributes: NSDictionary = try FileManager.default.attributesOfItem(atPath: fullPath) as NSDictionary
if fileAttributes.fileType() == "NSFileTypeRegular" {
return fileAttributes.fileSize()
}
let url = NSURL(fileURLWithPath: fullPath)
guard let directoryEnumerator = FileManager.default.enumerator(at: url as URL, includingPropertiesForKeys: [URLResourceKey.fileSizeKey], options: [.skipsHiddenFiles], errorHandler: nil) else { throw FileErrors.BadEnumeration }
var total: UInt64 = 0
for (index, object) in directoryEnumerator.enumerated() {
guard let fileURL = object as? NSURL else { throw FileErrors.BadResource }
var fileSizeResource: AnyObject?
try fileURL.getResourceValue(&fileSizeResource, forKey: URLResourceKey.fileSizeKey)
guard let fileSize = fileSizeResource as? NSNumber else { continue }
total += fileSize.uint64Value
if index % 1000 == 0 {
print(".", terminator: "")
}
}
if total < 1048576 {
total = 1
}
else
{
total = UInt64(total / 1048576)
}
return total
}
enum FileErrors : ErrorType {
case BadEnumeration
case BadResource
}
输出值为兆字节。
从源代码转换而来:https://gist.github.com/rayfix/66b0a822648c87326645
Swift 3 个版本
private func sizeToPrettyString(size: UInt64) -> String {
let byteCountFormatter = ByteCountFormatter()
byteCountFormatter.allowedUnits = .useMB
byteCountFormatter.countStyle = .file
let folderSizeToDisplay = byteCountFormatter.string(fromByteCount: Int64(size))
return folderSizeToDisplay
}
任何正在为 Swift 5+ 和 Xcode 11+ 寻找解决方案的人请看这个 gist
func directorySize(url: URL) -> Int64 {
let contents: [URL]
do {
contents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: [.fileSizeKey, .isDirectoryKey])
} catch {
return 0
}
var size: Int64 = 0
for url in contents {
let isDirectoryResourceValue: URLResourceValues
do {
isDirectoryResourceValue = try url.resourceValues(forKeys: [.isDirectoryKey])
} catch {
continue
}
if isDirectoryResourceValue.isDirectory == true {
size += directorySize(url: url)
} else {
let fileSizeResourceValue: URLResourceValues
do {
fileSizeResourceValue = try url.resourceValues(forKeys: [.fileSizeKey])
} catch {
continue
}
size += Int64(fileSizeResourceValue.fileSize ?? 0)
}
}
return size
}
对于寻找准系统实现的任何人(在 macOS 和 iOS 上工作相同):
Swift 5准系统版本
extension URL {
var fileSize: Int? { // in bytes
do {
let val = try self.resourceValues(forKeys: [.totalFileAllocatedSizeKey, .fileAllocatedSizeKey])
return val.totalFileAllocatedSize ?? val.fileAllocatedSize
} catch {
print(error)
return nil
}
}
}
extension FileManager {
func directorySize(_ dir: URL) -> Int? { // in bytes
if let enumerator = self.enumerator(at: dir, includingPropertiesForKeys: [.totalFileAllocatedSizeKey, .fileAllocatedSizeKey], options: [], errorHandler: { (_, error) -> Bool in
print(error)
return false
}) {
var bytes = 0
for case let url as URL in enumerator {
bytes += url.fileSize ?? 0
}
return bytes
} else {
return nil
}
}
}
用法
let fm = FileManager.default
let tmp = fm.temporaryDirectory
if let size = fm.directorySize(tmp) {
print(size)
}
是什么让这个准系统:不预先检查目录是目录还是文件是文件(returns nil
无论哪种方式),结果以其原始格式返回(字节为整数)。
我正在尝试使用 Swift 获取目录的大小及其在 OS X 上的内容。到目前为止,我只能获得目录本身的大小及其内容的 none 。对于我的大多数目录,它通常显示 6,148 字节的值,但它确实有所不同。
我尝试了下面文件中的 directorySize() 函数,但它也返回了 6,148 字节。
我尝试了这个问题的前 2 个答案,但不确定它需要将什么参数传递给 Swift 到 Objective-C 函数。我相信它需要一个指针(我是学习中的初学者)。
Calculate the size of a folder
而且我也无法从这里得到 Swift 答案以达到我的目的。
How to get the file size given a path?
我正在使用 Xcode 7.0 和 运行 OS X 10.10.5.
更新:Xcode 11.4.1 • Swift 5.2
extension URL {
/// check if the URL is a directory and if it is reachable
func isDirectoryAndReachable() throws -> Bool {
guard try resourceValues(forKeys: [.isDirectoryKey]).isDirectory == true else {
return false
}
return try checkResourceIsReachable()
}
/// returns total allocated size of a the directory including its subFolders or not
func directoryTotalAllocatedSize(includingSubfolders: Bool = false) throws -> Int? {
guard try isDirectoryAndReachable() else { return nil }
if includingSubfolders {
guard
let urls = FileManager.default.enumerator(at: self, includingPropertiesForKeys: nil)?.allObjects as? [URL] else { return nil }
return try urls.lazy.reduce(0) {
(try .resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize ?? 0) + [=10=]
}
}
return try FileManager.default.contentsOfDirectory(at: self, includingPropertiesForKeys: nil).lazy.reduce(0) {
(try .resourceValues(forKeys: [.totalFileAllocatedSizeKey])
.totalFileAllocatedSize ?? 0) + [=10=]
}
}
/// returns the directory total size on disk
func sizeOnDisk() throws -> String? {
guard let size = try directoryTotalAllocatedSize(includingSubfolders: true) else { return nil }
URL.byteCountFormatter.countStyle = .file
guard let byteCount = URL.byteCountFormatter.string(for: size) else { return nil}
return byteCount + " on disk"
}
private static let byteCountFormatter = ByteCountFormatter()
}
用法:
do {
let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
if let sizeOnDisk = try documentDirectory.sizeOnDisk() {
print("Size:", sizeOnDisk) // Size: 3.15 GB on disk
}
} catch {
print(error)
}
Swift 3 版本在这里:
func findSize(path: String) throws -> UInt64 {
let fullPath = (path as NSString).expandingTildeInPath
let fileAttributes: NSDictionary = try FileManager.default.attributesOfItem(atPath: fullPath) as NSDictionary
if fileAttributes.fileType() == "NSFileTypeRegular" {
return fileAttributes.fileSize()
}
let url = NSURL(fileURLWithPath: fullPath)
guard let directoryEnumerator = FileManager.default.enumerator(at: url as URL, includingPropertiesForKeys: [URLResourceKey.fileSizeKey], options: [.skipsHiddenFiles], errorHandler: nil) else { throw FileErrors.BadEnumeration }
var total: UInt64 = 0
for (index, object) in directoryEnumerator.enumerated() {
guard let fileURL = object as? NSURL else { throw FileErrors.BadResource }
var fileSizeResource: AnyObject?
try fileURL.getResourceValue(&fileSizeResource, forKey: URLResourceKey.fileSizeKey)
guard let fileSize = fileSizeResource as? NSNumber else { continue }
total += fileSize.uint64Value
if index % 1000 == 0 {
print(".", terminator: "")
}
}
if total < 1048576 {
total = 1
}
else
{
total = UInt64(total / 1048576)
}
return total
}
enum FileErrors : ErrorType {
case BadEnumeration
case BadResource
}
输出值为兆字节。 从源代码转换而来:https://gist.github.com/rayfix/66b0a822648c87326645
Swift 3 个版本
private func sizeToPrettyString(size: UInt64) -> String {
let byteCountFormatter = ByteCountFormatter()
byteCountFormatter.allowedUnits = .useMB
byteCountFormatter.countStyle = .file
let folderSizeToDisplay = byteCountFormatter.string(fromByteCount: Int64(size))
return folderSizeToDisplay
}
任何正在为 Swift 5+ 和 Xcode 11+ 寻找解决方案的人请看这个 gist
func directorySize(url: URL) -> Int64 {
let contents: [URL]
do {
contents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: [.fileSizeKey, .isDirectoryKey])
} catch {
return 0
}
var size: Int64 = 0
for url in contents {
let isDirectoryResourceValue: URLResourceValues
do {
isDirectoryResourceValue = try url.resourceValues(forKeys: [.isDirectoryKey])
} catch {
continue
}
if isDirectoryResourceValue.isDirectory == true {
size += directorySize(url: url)
} else {
let fileSizeResourceValue: URLResourceValues
do {
fileSizeResourceValue = try url.resourceValues(forKeys: [.fileSizeKey])
} catch {
continue
}
size += Int64(fileSizeResourceValue.fileSize ?? 0)
}
}
return size
}
对于寻找准系统实现的任何人(在 macOS 和 iOS 上工作相同):
Swift 5准系统版本
extension URL {
var fileSize: Int? { // in bytes
do {
let val = try self.resourceValues(forKeys: [.totalFileAllocatedSizeKey, .fileAllocatedSizeKey])
return val.totalFileAllocatedSize ?? val.fileAllocatedSize
} catch {
print(error)
return nil
}
}
}
extension FileManager {
func directorySize(_ dir: URL) -> Int? { // in bytes
if let enumerator = self.enumerator(at: dir, includingPropertiesForKeys: [.totalFileAllocatedSizeKey, .fileAllocatedSizeKey], options: [], errorHandler: { (_, error) -> Bool in
print(error)
return false
}) {
var bytes = 0
for case let url as URL in enumerator {
bytes += url.fileSize ?? 0
}
return bytes
} else {
return nil
}
}
}
用法
let fm = FileManager.default
let tmp = fm.temporaryDirectory
if let size = fm.directorySize(tmp) {
print(size)
}
是什么让这个准系统:不预先检查目录是目录还是文件是文件(returns nil
无论哪种方式),结果以其原始格式返回(字节为整数)。