Holen Sie sich die Dateigröße in Swift

73

Ich habe verschiedene Methoden ausprobiert, um die Dateigröße zu ermitteln, aber immer Null.

let path = NSBundle.mainBundle().pathForResource("movie", ofType: "mov")
let attr = NSFileManager.defaultManager().attributesOfFileSystemForPath(path!, error: nil)
if let attr = attr {
    let size: AnyObject? = attr[NSFileSize]
    println("File size = \(size)")
}

Ich bekomme im Log: File size = nil

Gralex
quelle
Sind Sie sicher, dass sich die Datei im App-Bundle auf der obersten Ebene befindet?
Zaph
4
Oder versuchen Sie, AttributeOfItemAtPath anstelle von AttributenOfFileSystemForPath zu verwenden. Sie können .fileSize () auf attr aufrufen, um die Dateigröße abzurufen.
Duyen-Hoa
1
@ tyt_g207 Ich glaube, das ist die richtige Lösung, aber Sie sollten sie wahrscheinlich als Antwort anstelle eines Kommentars ausschreiben, damit zukünftige Antwortsuchende sie leichter finden können.
Lyndsey Scott

Antworten:

128

Verwenden Sie attributesOfItemAtPathanstelle von attributesOfFileSystemForPath + .fileSize () auf Ihrem attr.

var filePath: NSString = "your path here"
var fileSize : UInt64
var attr:NSDictionary? = NSFileManager.defaultManager().attributesOfItemAtPath(filePath, error: nil)
if let _attr = attr {
    fileSize = _attr.fileSize();
}

In Swift 2.0 verwenden wir das folgende Fangmuster:

let filePath = "your path here"
var fileSize : UInt64 = 0

do {
    let attr : NSDictionary? = try NSFileManager.defaultManager().attributesOfItemAtPath(filePath)

    if let _attr = attr {
        fileSize = _attr.fileSize();
    }
} catch {
    print("Error: \(error)")
}

In Swift 3.x / 4.0:

let filePath = "your path here"
var fileSize : UInt64

do {
    //return [FileAttributeKey : Any]
    let attr = try FileManager.default.attributesOfItem(atPath: filePath)
    fileSize = attr[FileAttributeKey.size] as! UInt64

    //if you convert to NSDictionary, you can get file size old way as well.
    let dict = attr as NSDictionary
    fileSize = dict.fileSize()
} catch {
    print("Error: \(error)")
}
Duyen-Hoa
quelle
3
ist es nur ich, der denkt, dass Swift 2 die Dinge nur noch viel schlimmer gemacht hat. Ich war vollkommen zufrieden mit & error. try-catch, die keine Ausdrücke sind, ist scheiße.
Martin Algesten
1
Sie können jederzeit try?eine optionale Rücksendung verwenden und erhalten.
HAT
1
Was ist das Ergebnis? meins war zB 32768.0ist das Byte? so ziemlich 32KB?
David Seek
1
@BrandonA es ist in Byte
Duyen-Hoa
36

Swift4: URL-Erweiterung für den einfachen Zugriff auf Dateiattribute

Erweiterung:

extension URL {
    var attributes: [FileAttributeKey : Any]? {
        do {
            return try FileManager.default.attributesOfItem(atPath: path)
        } catch let error as NSError {
            print("FileAttribute error: \(error)")
        }
        return nil
    }

    var fileSize: UInt64 {
        return attributes?[.size] as? UInt64 ?? UInt64(0)
    }

    var fileSizeString: String {
        return ByteCountFormatter.string(fromByteCount: Int64(fileSize), countStyle: .file)
    }

    var creationDate: Date? {
        return attributes?[.creationDate] as? Date
    }
}

Verwendung:

let fileUrl: URL
print("file size = \(fileUrl.fileSize), \(fileUrl.fileSizeString)")
Gheclipse
quelle
1
URLstellt die Dateiattribute direkt mit bereit resourceValues(forKeys.
Vadian
Dies ist die beste Antwort. Sollte die akzeptierte Antwort sein
Ahufford
19

SWIFT 3 stammt aus der Antwort von @ Hoa und plus einer Funktion, die UInt64 zu einem lesbaren String macht.

func sizeForLocalFilePath(filePath:String) -> UInt64 {
    do {
        let fileAttributes = try FileManager.default.attributesOfItem(atPath: filePath)
        if let fileSize = fileAttributes[FileAttributeKey.size]  {
            return (fileSize as! NSNumber).uint64Value
        } else {
            print("Failed to get a size attribute from path: \(filePath)")
        }
    } catch {
        print("Failed to get file attributes for local path: \(filePath) with error: \(error)")
    }
    return 0
}
func covertToFileString(with size: UInt64) -> String {
    var convertedValue: Double = Double(size)
    var multiplyFactor = 0
    let tokens = ["bytes", "KB", "MB", "GB", "TB", "PB",  "EB",  "ZB", "YB"]
    while convertedValue > 1024 {
        convertedValue /= 1024
        multiplyFactor += 1
    }
    return String(format: "%4.2f %@", convertedValue, tokens[multiplyFactor])
}
Hieronymus
quelle
2
Funktioniert gut, bis auf einen Fehler bei covertToFileString. Ersetzen Sie while size > 1024durch while convertedValue > 1024, um eine Endlosschleife zu vermeiden
Nicolas Lauquin
16

In Swift 3+ können Sie die Dateigröße direkt von der URL abrufen, (NS)FileManagerwird nicht benötigt. Und ByteCountFormatterist eine clevere Möglichkeit, die Dateigröße anzuzeigen.

let url = Bundle.main.url(forResource:"movie", withExtension: "mov")!
do {
    let resourceValues = try url.resourceValues(forKeys: [.fileSizeKey])
    let fileSize = resourceValues.fileSize!
    print("File size = " + ByteCountFormatter().string(fromByteCount: Int64(fileSize)))
} catch { print(error) }

Eigentlich kann man die Dateigröße URLsogar in Swift 2 abrufen, aber die Syntax ist etwas umständlicher.

vadian
quelle
9

Hier ist die Antwort von Biodave mit dem richtigen Dateimanager-Aufruf

func sizeForLocalFilePath(filePath:String) -> UInt64 {
    do {
        let fileAttributes = try NSFileManager.defaultManager().attributesOfItemAtPath(filePath)
        if let fileSize = fileAttributes[NSFileSize]  {
            return (fileSize as! NSNumber).unsignedLongLongValue
        } else {
            print("Failed to get a size attribute from path: \(filePath)")
        }
    } catch {
        print("Failed to get file attributes for local path: \(filePath) with error: \(error)")
    }
    return 0
}

Für Swift 4.2:

func sizeForLocalFilePath(filePath:String) -> UInt64 {
    do {
        let fileAttributes = try FileManager.default.attributesOfItem(atPath: filePath)
        if let fileSize = fileAttributes[FileAttributeKey.size]  {
            return (fileSize as! NSNumber).uint64Value
        } else {
            print("Failed to get a size attribute from path: \(filePath)")
        }
    } catch {
        print("Failed to get file attributes for local path: \(filePath) with error: \(error)")
    }
    return 0
}
Guy Brooker
quelle
8

Swift 4-Lösung: Diese Funktion gibt die MB-Größe zurück.

func sizePerMB(url: URL?) -> Double {
    guard let filePath = url?.path else {
        return 0.0
    }
    do {
        let attribute = try FileManager.default.attributesOfItem(atPath: filePath)
        if let size = attribute[FileAttributeKey.size] as? NSNumber {
            return size.doubleValue / 1000000.0
        }
    } catch {
        print("Error: \(error)")
    }
    return 0.0
}
Ahmed Lotfy
quelle
Ich bin wirklich gestolpert, bis ich das sage. Hat fast das Gleiche getan, aber die Pfadeigenschaft der URL nicht verwendet. Danke, Kumpel!
Felipe
Wie kann man dafür sorgen, dass die KB-Größe zurückgegeben wird?
Khodour.F
7

Hier sind zwei weitere verschiedene Implementierungen mit Swift 4 , eine ist detailliert formatiert und die andere ist dezimal formatiert.

Der mit NumberFomatter:

func fileSize(fromPath path: String) -> String? {
    var size: Any?
    do {
        size = try FileManager.default.attributesOfItem(atPath: path)[FileAttributeKey.size]
    } catch (let error) {
        print("File size error: \(error)")
        return nil
    }
    guard let fileSize = size as? UInt64 else {
        return nil
    }

    let formatter = NumberFormatter()
    formatter.numberStyle = .decimal
    formatter.formatterBehavior = .behavior10_4
    return formatter.string(from: NSNumber(value: fileSize))
}

Und der andere definiert mit Größeneinheiten:

func fileSize(fromPath path: String) -> String? {
    guard let size = try? FileManager.default.attributesOfItem(atPath: path)[FileAttributeKey.size],
        let fileSize = size as? UInt64 else {
        return nil
    }

    // bytes
    if fileSize < 1023 {
        return String(format: "%lu bytes", CUnsignedLong(fileSize))
    }
    // KB
    var floatSize = Float(fileSize / 1024)
    if floatSize < 1023 {
        return String(format: "%.1f KB", floatSize)
    }
    // MB
    floatSize = floatSize / 1024
    if floatSize < 1023 {
        return String(format: "%.1f MB", floatSize)
    }
    // GB
    floatSize = floatSize / 1024
    return String(format: "%.1f GB", floatSize)
}
abdullahselek
quelle
6

Versuche dies.

let MyUrl = NSURL(fileURLWithPath: "*** Custom File Path ***")                  
let fileAttributes = try! NSFileManager.defaultManager().attributesOfItemAtPath(MyUrl.path!)
let fileSizeNumber = fileAttributes[NSFileSize] as! NSNumber
let fileSize = fileSizeNumber.longLongValue
var sizeMB = Double(fileSize / 1024)
sizeMB = Double(sizeMB / 1024)
print(String(format: "%.2f", sizeMB) + " MB")
Programer_saeed
quelle
6

URL-Erweiterung in Swift, um die Dateigröße zu ermitteln, falls vorhanden.

public extension URL {

    var fileSize: Int? {
        let value = try? resourceValues(forKeys: [.fileSizeKey])
        return value?.fileSize
    }
}

Der Grund für die Rückgabe eines optionalen Int liegt darin, dass eine unbekannte Dateigröße nicht als Null angesehen werden kann.

Boherna
quelle
1

Versuchen Sie in Swift 3.0 Folgendes:

let fileSize = try! FileManager.default.attributesOfItem(atPath: "/bin/bash")[FileAttributeKey.size] as! Int

oder noch besser:

let fileSize = (try! FileManager.default.attributesOfItem(atPath: "/bin/bash")[FileAttributeKey.size] as! NSNumber).uint64Value
AlexV
quelle
Wie ist der zweite besser? Warum sollte man die automatische NSNumber-Konvertierung nicht verwenden? Auch sollten Sie versuchen? und wie? mit if- oder guard-Anweisung als Dateioperation kann fehlschlagen.
Tapani
1

Inspiration von ein paar anderen Antworten.

extension URL {
    var filesize: Int? {
        let set = Set.init([URLResourceKey.fileSizeKey])
        var filesize: Int?      
        do {
            let values = try self.resourceValues(forKeys: set)
            if let theFileSize = values.fileSize {
                filesize = theFileSize
            }
        }
        catch {
            print("Error: \(error)")
        }
        return filesize
    }

    var filesizeNicelyformatted: String? {
        guard let fileSize = self.filesize else {
            return nil
        }
        return ByteCountFormatter.init().string(fromByteCount: Int64(fileSize))
    }
}
Jonny
quelle
0

Hier ist eine kompakte Version, die in Swift 2.0 für iOS9 über Xcode7 als Happy-Methode geschrieben wurde:

func sizeForLocalFilePath(filePath:String) -> UInt64 {
    do {
        let fileAttributes = try NSFileManager().attributesOfFileSystemForPath(filePath)
        if let fileSize = fileAttributes[NSFileSystemSize] as? UInt64 {
            return fileSize
        } else {
            print("Failed to get a size attribute from path: \(filePath)")
        }
    } catch {
        print("Failed to get file attributes for local path: \(filePath) with error: \(error)")
    }
    return 0
}

Genießen!

Biodave
quelle
Das funktioniert nicht. Es überprüft die Dateisystemattribute, nicht das Attribut der Datei selbst.
Guy Brooker