How To Get Directory Size With Swift On OS X

前端 未结 5 1288
孤独总比滥情好
孤独总比滥情好 2020-12-15 13:04

I am trying to get the size of a directory, as well as it\'s content on OS X using Swift. So far, I have only been able to get the size of the directory itself, with none of

相关标签:
5条回答
  • 2020-12-15 13:12

    update: 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 $1.resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize ?? 0) + $0
                }
            }
            return try FileManager.default.contentsOfDirectory(at: self, includingPropertiesForKeys: nil).lazy.reduce(0) {
                     (try $1.resourceValues(forKeys: [.totalFileAllocatedSizeKey])
                        .totalFileAllocatedSize ?? 0) + $0
            }
        }
    
        /// 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()
    }
    

    usage:

    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)
    }
    
    0 讨论(0)
  • 2020-12-15 13:12

    Swift 3 version

    private func sizeToPrettyString(size: UInt64) -> String {
    
        let byteCountFormatter = ByteCountFormatter()
        byteCountFormatter.allowedUnits = .useMB
        byteCountFormatter.countStyle = .file
        let folderSizeToDisplay = byteCountFormatter.string(fromByteCount: Int64(size))
    
        return folderSizeToDisplay
    
    }
    
    0 讨论(0)
  • 2020-12-15 13:17

    For anyone looking for the barebones implementation (works the same on macOS and iOS):

    Swift 5 barebones version

    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
            }
        }
    }
    

    Usage

    let fm = FileManager.default
    let tmp = fm.temporaryDirectory
    
    if let size = fm.directorySize(tmp) {
        print(size)
    }
    

    What makes this barebones: doesn't precheck if a directory is a directory or a file is a file (returns nil either way), and the results are returned in their native format (bytes as integers).

    0 讨论(0)
  • 2020-12-15 13:22

    To anyone who is looking for a solution for Swift 5+ and Xcode 11+ look at this 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
    

    }

    0 讨论(0)
  • 2020-12-15 13:23

    Swift 3 version here:

     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
    }
    

    Output value is megabyte. Converted from source: https://gist.github.com/rayfix/66b0a822648c87326645

    0 讨论(0)
提交回复
热议问题