2016-07-13 9 views

Odpowiedz

17

Oto możliwe wdrożenie w Swift 3 jako rozszerzenie dla URL, z metod, aby uzyskać, zestaw, listy i usunąć rozszerzone atrybuty akta. (Swift i kod 2 można znaleźć poniżej.)

extension URL { 

    /// Get extended attribute. 
    func extendedAttribute(forName name: String) throws -> Data { 

     let data = try self.withUnsafeFileSystemRepresentation { fileSystemPath -> Data in 

      // Determine attribute size: 
      let length = getxattr(fileSystemPath, name, nil, 0, 0, 0) 
      guard length >= 0 else { throw URL.posixError(errno) } 

      // Create buffer with required size: 
      var data = Data(count: length) 

      // Retrieve attribute: 
      let result = data.withUnsafeMutableBytes { 
       getxattr(fileSystemPath, name, $0, data.count, 0, 0) 
      } 
      guard result >= 0 else { throw URL.posixError(errno) } 
      return data 
     } 
     return data 
    } 

    /// Set extended attribute. 
    func setExtendedAttribute(data: Data, forName name: String) throws { 

     try self.withUnsafeFileSystemRepresentation { fileSystemPath in 
      let result = data.withUnsafeBytes { 
       setxattr(fileSystemPath, name, $0, data.count, 0, 0) 
      } 
      guard result >= 0 else { throw URL.posixError(errno) } 
     } 
    } 

    /// Remove extended attribute. 
    func removeExtendedAttribute(forName name: String) throws { 

     try self.withUnsafeFileSystemRepresentation { fileSystemPath in 
      let result = removexattr(fileSystemPath, name, 0) 
      guard result >= 0 else { throw URL.posixError(errno) } 
     } 
    } 

    /// Get list of all extended attributes. 
    func listExtendedAttributes() throws -> [String] { 

     let list = try self.withUnsafeFileSystemRepresentation { fileSystemPath -> [String] in 
      let length = listxattr(fileSystemPath, nil, 0, 0) 
      guard length >= 0 else { throw URL.posixError(errno) } 

      // Create buffer with required size: 
      var data = Data(count: length) 

      // Retrieve attribute list: 
      let result = data.withUnsafeMutableBytes { 
       listxattr(fileSystemPath, $0, data.count, 0) 
      } 
      guard result >= 0 else { throw URL.posixError(errno) } 

      // Extract attribute names: 
      let list = data.split(separator: 0).flatMap { 
       String(data: Data($0), encoding: .utf8) 
      } 
      return list 
     } 
     return list 
    } 

    /// Helper function to create an NSError from a Unix errno. 
    private static func posixError(_ err: Int32) -> NSError { 
     return NSError(domain: NSPOSIXErrorDomain, code: Int(err), 
         userInfo: [NSLocalizedDescriptionKey: String(cString: strerror(err))]) 
    } 
} 

Przykład użycia:

let fileURL = URL(fileURLWithPath: "/path/to/file") 

let attr1 = "com.myCompany.myAttribute" 
let attr2 = "com.myCompany.otherAttribute" 

let data1 = Data(bytes: [1, 2, 3, 4]) 
let data2 = Data(bytes: [5, 6, 7, 8, 9]) 

do { 
    // Set attributes: 
    try fileURL.setExtendedAttribute(data: data1, forName: attr1) 
    try fileURL.setExtendedAttribute(data: data2, forName: attr2) 

    // List attributes: 
    let list = try fileURL.listExtendedAttributes() 
    print(list) 
    // ["com.myCompany.myAttribute", "com.myCompany.otherAttribute", "other"] 

    let data1a = try fileURL.extendedAttribute(forName: attr1) 
    print(data1a as NSData) 
    // <01020304> 

    // Remove attributes 
    for attr in list { 
     try fileURL.removeExtendedAttribute(forName: attr) 
    } 

} catch let error { 
    print(error.localizedDescription) 
} 

(poprzedniej odpowiedzi na szybkie 2 :)

prostym przykładzie:

let fileURL = NSURL(fileURLWithPath: "/path/to/file") 
let attrName = "com.myCompany.myAttribute" 
var attrData: [UInt8] = [1, 2, 3, 4, 5] 

// Get local file system path: 
var fileSystemPath = [Int8](count: Int(MAXPATHLEN), repeatedValue: 0) 
guard fileURL.getFileSystemRepresentation(&fileSystemPath, maxLength: fileSystemPath.count) else { 
    fatalError("Invalid file URL") 
} 

// Set extended attribute (returns 0 on success): 
guard setxattr(&fileSystemPath, attrName, &attrData, attrData.count, 0, 0) == 0 else { 
    perror("setxattr") // prints error message of last system call 
    fatalError() 
} 

Sprawdź wynik w wierszu poleceń:

 
$ xattr -px com.myCompany.myAttribute /path/to/file 
01 02 03 04 05 

A oto w jaki sposób można pobrać i ustawić atrybuty pliku, realizowany jako metod rozszerzających NSURL:

extension NSURL { 
    // Get extended attribute. Returns `nil` on failure. 
    func extendedAttribute(forName name: String) -> [UInt8]? { 
     // Get local file system path: 
     var fileSystemPath = [Int8](count: Int(MAXPATHLEN), repeatedValue: 0) 
     guard self.getFileSystemRepresentation(&fileSystemPath, maxLength: fileSystemPath.count) else { 
      return nil 
     } 
     // Determine attribute size: 
     let length = getxattr(fileSystemPath, name, nil, 0, 0, 0) 
     guard length >= 0 else { return nil } 
     // Create buffer with required size: 
     var data = [UInt8](count: length, repeatedValue: 0) 
     // Retrieve attribute: 
     let result = getxattr(fileSystemPath, name, &data, data.count, 0, 0) 
     guard result >= 0 else { return nil } 
     return data 
    } 

    // Set extended attribute. Returns `true` on success and `false` on failure. 
    func setExtendedAttribute(data: [UInt8], forName name: String) -> Bool { 
     // Get local file system path: 
     var fileSystemPath = [Int8](count: Int(MAXPATHLEN), repeatedValue: 0) 
     guard self.getFileSystemRepresentation(&fileSystemPath, maxLength: fileSystemPath.count) else { 
      return false 
     } 
     // Set attribute: 
     let result = data.withUnsafeBufferPointer { 
      setxattr(fileSystemPath, name, $0.baseAddress, data.count, 0, 0) 
     } 
     return result == 0 
    } 

    // Remove extended attribute. Returns `true` on success and `false` on failure. 
    func removeExtendedAttribute(forName name: String) -> Bool { 
     // Get local file system path: 
     var fileSystemPath = [Int8](count: Int(MAXPATHLEN), repeatedValue: 0) 
     guard self.getFileSystemRepresentation(&fileSystemPath, maxLength: fileSystemPath.count) else { 
      return false 
     } 
     // Remove attribute: 
     let result = removexattr(fileSystemPath, name, 0) 
     return result == 0 
    } 
} 

Przykład użycia:

let fileURL = NSURL(fileURLWithPath: "/path/to/file") 
let attrName = "com.myCompany.myAttribute" 

// Set attribute: 
let data1: [UInt8] = [1, 2, 3, 4] 
fileURL.setExtendedAttribute(data1, forName: attrName) 

// Get attribute: 
if let data2 = fileURL.extendedAttribute(forName: attrName) { 
    print(data2) 
} 

// Remove attribute: 
fileURL.removeExtendedAttribute(forName: attrName) 
+0

Dzięki za odpowiedź. Po prostu skopiowałem twój kod i uruchomiłem aplikację. Nie wydrukowano żadnych błędów, ale nie mogłem potwierdzić zestawu atrybutów z wiersza poleceń. Otrzymałem komunikat "Brak takiego xattr: com.myCompany.myAttribute" – prabhu

+0

@prabhu: Czy dwukrotnie sprawdziłeś, czy nazwa atrybutu w kodzie jest taka sama jak nazwa atrybutu w linii poleceń? Za pomocą 'xattr/path/to/file' możesz wyświetlić listę wszystkich istniejących atrybutów pliku. - Udało mi się, przetestowałem kod przed wysłaniem :) –

+0

Tak, to jest tak samo jak w twoim przykładzie. Nie jestem pewien, czy czegoś brakuje. Ale po prostu kopiuję wkleiłem wszystko, co napisałeś. Bez zmian – prabhu

0

Rozumiem, że prosiłeś o rozwiązanie w Swift. Jednak niektórzy ludzie mogą mieć hybrydowy projekt Swift/ObjC lub być gotowym na taki projekt. W moim przypadku znacznie łatwiej było użyć Objective-C do tego jednego zadania.

#import <Foundation/Foundation.h> 

@interface TagSetter: NSObject 
+ (BOOL)setTags:(NSArray *)tags onURL:(NSURL *)url; 
@end 

@implementation TagSetter 

+ (BOOL)setTags:(NSArray *)tags onURL:(NSURL *)url 
{ 
    NSError *error; 

    BOOL returnValue = [url setResourceValue:tags forKey:NSURLTagNamesKey error:&error]; 
    if (!returnValue) { 
     NSLog(@"ERROR: Failed setting tags '%@' on URL '%@': %@", tags, url, error); 
    } 

    return returnValue; 
} 

@end 

Kod ten jest przystosowany od tag(1) command line tool przez jdberry na GitHub. Potem następuje zgodnie z instrukcjami dla using ObjC in Swift i dodaje plik .m wyżej nagłówku pomostowego:

#import "TagSetter.m" 

W końcu wszystko, co mam zrobić w kodzie SWIFT:

TagSetter.setTags(tagNames, on:url) 

Jeśli chcesz odpowiedzieć na niepowodzenie, możesz.

To od Ciebie zależy, czy to podejście okaże się proste, czy lekko hacky, czy przerażająca przesada. Ale na pewno działa i daje tylko jedną linię Swift.