使用FileManager记录文件上次打开时间时自定义FileAttributeKey失效如何解决
问题根因
你自定义的FileAttributeKey无法持久化的原因是:FileManager.setAttributes(_:ofItemAtPath:)仅支持系统预定义的标准文件属性键,自定义的键值不会被写入到文件系统元数据中,所以二次读取时自然获取不到对应值。
推荐解决方案
方案1:使用扩展属性(xattr,跨平台通用,支持持久化自定义元数据)
扩展属性是文件系统级别的自定义元数据存储方案,可以持久化保存任意自定义键值,适合给文件附加独立于文件内容的标记信息。
Swift读写示例:
写入上次打开时间
import Foundation func setLastOpenedDate(_ date: Date, forPath path: String) throws { let data = try JSONEncoder().encode(date) // 扩展属性名建议加App专属前缀避免冲突 let attrName = "com.yourapp.lastOpenedAt" try data.withUnsafeBytes { buffer in let result = setxattr(path, attrName, buffer.baseAddress, buffer.count, 0, 0) if result == -1 { throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno), userInfo: nil) } } } // 调用示例 do { try setLastOpenedDate(Date(), forPath: videoNameDirectoryPath) } catch { Log.error(error.localizedDescription) }
读取上次打开时间
func getLastOpenedDate(forPath path: String) throws -> Date? { let attrName = "com.yourapp.lastOpenedAt" // 先获取属性长度 let length = getxattr(path, attrName, nil, 0, 0, 0) guard length != -1 else { // 不存在对应属性 return nil } var data = Data(count: length) let result = data.withUnsafeMutableBytes { buffer in getxattr(path, attrName, buffer.baseAddress, buffer.count, 0, 0) } guard result != -1 else { throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno), userInfo: nil) } return try JSONDecoder().decode(Date.self, from: data) }
适用场景:需要和其他App共享该标记、文件存储位置不固定的场景。
方案2:本地映射表存储
如果文件仅在你的App沙盒内使用,不需要对外共享打开时间标记,可以直接在本地维护一个文件路径(或者文件唯一标识)和打开时间的映射表,存储到UserDefaults或者独立的plist文件中。
优点是读写速度更快,不需要调用文件系统接口,适合文件数量不多的场景。
方案3:使用系统预定义的最后使用时间属性
macOS/iOS的Spotlight元数据里有kMDItemLastUsedDate键,记录了文件最后一次被用户打开的时间,无需自己维护,直接读取即可:
import CoreServices func getSystemLastUsedDate(forPath path: String) -> Date? { let url = URL(fileURLWithPath: path) guard let mdItem = MDItemCreateWithURL(kCFAllocatorDefault, url as CFURL) else { return nil } return MDItemCopyAttribute(mdItem, kMDItemLastUsedDate) as? Date }
注意:该属性仅对Spotlight索引覆盖范围内的文件有效,沙盒内部分目录、外置存储未被索引的文件可能读取不到。
内容的提问来源于stack exchange,提问作者Michael Jajou
相关产品推荐
相关产品推荐

