Mac OS应用程序中用户选择的文件夹的读写权限?

Posted

技术标签:

【中文标题】Mac OS应用程序中用户选择的文件夹的读写权限?【英文标题】:Read and write permission for user selected folder in Mac OS app? 【发布时间】:2017-12-20 09:43:32 【问题描述】:

我正在开发 MAC OS 应用程序,它具有代表您创建文件的功能。首先用户选择用于存储文件的文件夹(在应用程序启动时一次),然后用户可以使用苹果脚本选择用户想要在所选文件夹(应用程序启动时选择的文件夹)上创建的文件的类型和名称。当我在权利文件中添加下面的temporary-exception 时,我能够创建文件,但它不能应用苹果审查团队,但可以在沙盒中工作。

准则 2.4.5(i) - 性能 我们已确定为此应用请求的一项或多项临时权利例外不合适,因此不会被授予:

com.apple.security.temporary-exception.files.home-relative-path.read-write
/FolderName/

我找到了:

Enabling App Sandbox - 允许应用写入可执行文件。

还有

Enabling User-Selected File Access - Xcode 在目标编辑器的“摘要”选项卡中提供了一个弹出菜单,可以选择启用对用户明确选择的文件和文件夹的只读或读/写访问权限。启用用户选择的文件访问权限后,您将获得对用户使用 NSOpenPanel 对象打开的文件和文件夹以及用户使用 NSSavePanel 对象保存的文件的编程访问权限。

使用以下代码创建文件:

let str = "Super long string here"
let filename = getDocumentsDirectory().appendingPathComponent("/xyz/output.txt")

do 
    try str.write(to: filename, atomically: true, encoding: String.Encoding.utf8)
 catch 
    // failed to write file – bad permissions, bad filename, missing permissions, or more likely it can't be converted to the encoding

还尝试在 NSOpenPanel 对象的授权文件中添加 com.apple.security.files.user-selected.read-write

<key>com.apple.security.files.user-selected.read-write</key>
<true/>

有什么方法可以通过苹果审核团队来批准对用户选择的文件夹具有读写权限的 Mac App?

【问题讨论】:

如果用户总是选择目标文件夹(意思是,如果你的应用在没有用户操作的情况下不尝试写入)那么你只需要 com.apple.security.files.user-selected.read - 写,没有别的。 使用安全范围的书签。 @Moritz 是的,但我想要一次文件夹选择,然后每个文件都将存储在同一个位置。 那么你仍然只需要 com.apple.security.files.user-selected.read-write 但你必须使用安全范围的书签,正如 El Tomato 刚才所说的那样。 使用安全范围的书签。应用程序可以要求文件夹访问权限一次,然后单击按钮,他可以在同一位置创建和保存文档。 @El Tomato 请提供一些参考网站(developer.apple.com 除外) 【参考方案1】:

这是我的答案 如何在 Mac OS 应用中实现和持久化用户选择文件夹的读写权限?

GitHub Example Project link

第一:

在授权文件中添加user-selectedbookmarks.app 权限:

<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>

然后我为存储、加载...等书签应用程序所需的所有书签相关功能创建了类。

import Foundation
import Cocoa

var bookmarks = [URL: Data]()

func openFolderSelection() -> URL?

    let openPanel = NSOpenPanel()
    openPanel.allowsMultipleSelection = false
    openPanel.canChooseDirectories = true
    openPanel.canCreateDirectories = true
    openPanel.canChooseFiles = false
    openPanel.begin
         (result) -> Void in
            if result.rawValue == NSApplication.ModalResponse.OK.rawValue
            
                let url = openPanel.url
                storeFolderInBookmark(url: url!)
            
    
    return openPanel.url


func saveBookmarksData()

    let path = getBookmarkPath()
    NSKeyedArchiver.archiveRootObject(bookmarks, toFile: path)


func storeFolderInBookmark(url: URL)

    do
    
        let data = try url.bookmarkData(options: NSURL.BookmarkCreationOptions.withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil)
        bookmarks[url] = data
    
    catch
    
        Swift.print ("Error storing bookmarks")
    



func getBookmarkPath() -> String

    var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL
    url = url.appendingPathComponent("Bookmarks.dict")
    return url.path


func loadBookmarks()

    let path = getBookmarkPath()
    bookmarks = NSKeyedUnarchiver.unarchiveObject(withFile: path) as! [URL: Data]
    for bookmark in bookmarks
    
        restoreBookmark(bookmark)
    




func restoreBookmark(_ bookmark: (key: URL, value: Data))

    let restoredUrl: URL?
    var isStale = false

    Swift.print ("Restoring \(bookmark.key)")
    do
    
        restoredUrl = try URL.init(resolvingBookmarkData: bookmark.value, options: NSURL.BookmarkResolutionOptions.withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
    
    catch
    
        Swift.print ("Error restoring bookmarks")
        restoredUrl = nil
    

    if let url = restoredUrl
    
        if isStale
        
            Swift.print ("URL is stale")
        
        else
        
            if !url.startAccessingSecurityScopedResource()
            
                Swift.print ("Couldn't access: \(url.path)")
            
        
    


然后使用NSOpenPanel 打开文件夹选择,以便用户可以选择允许您访问的文件夹。 NSOpenPanel 必须存储为书签并保存到磁盘。然后,您的应用将拥有与用户选择文件夹时相同的访问级别。

打开NSOpenPanel

let selectedURL = openFolderSelection()
saveBookmarksData()

并在应用关闭后加载现有书签:

loadBookmarks()

就是这样。 我希望它会对某人有所帮助。

【讨论】:

除了user-selectedmoviespictures等之外,我们可以定义自定义文件夹read/write访问吗?OK likeDocuments Sid 看起来很有趣,但我无法编译您的 GitHub 项目,因为它在 func loadBookmarks() 中崩溃。 安迪,你是对的。我编辑了代码以解决该问题。基本上,如果从未保存过书签,那么您将无法加载它们并强制展开。【参考方案2】:

在授权文件中添加user-selectedbookmarks.app 权限:

<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
<true/>

然后使用NSOpenPanel 打开文件夹选择,以便用户可以选择允许您访问的文件夹。 NSOpenPanel 必须存储为书签并保存到磁盘。然后,您的应用将拥有与用户选择文件夹时相同的访问级别。

【讨论】:

感谢您的回复,我会试试这个。【参考方案3】:

我在这里找到了最佳且有效的答案 - reusing security scoped bookmark

超级简单,易于理解并且做得很好。

解决方案是:-

var userDefault = NSUserDefaults.standardUserDefaults()
var folderPath: NSURL? 
    didSet 
        do 
            let bookmark = try folderPath?.bookmarkDataWithOptions(.SecurityScopeAllowOnlyReadAccess, includingResourceValuesForKeys: nil, relativeToURL: nil)
            userDefault.setObject(bookmark, forKey: "bookmark")
         catch let error as NSError 
            print("Set Bookmark Fails: \(error.description)")
        
    


func applicationDidFinishLaunching(aNotification: NSNotification) 
    if let bookmarkData = userDefault.objectForKey("bookmark") as? NSData 
        do 
            let url = try NSURL.init(byResolvingBookmarkData: bookmarkData, options: .WithoutUI, relativeToURL: nil, bookmarkDataIsStale: nil)
            url.startAccessingSecurityScopedResource()
         catch let error as NSError 
            print("Bookmark Access Fails: \(error.description)")
        
    

【讨论】:

【参考方案4】:

已更新至 Swift 5(感谢 Jay!)

var folderPath: URL? 
    didSet 
        do 
            let bookmark = try folderPath?.bookmarkData(options: .securityScopeAllowOnlyReadAccess, includingResourceValuesForKeys: nil, relativeTo: nil)
            UserDefaults.standard.set(bookmark, forKey: "bookmark")
         catch let error as NSError 
            print("Set Bookmark Fails: \(error.description)")
        
    


 func applicationDidFinishLaunching(_ aNotification: Notification) 
        if let bookmarkData = UserDefaults.standard.object(forKey: "bookmark") as? Data 
            do 
                var bookmarkIsStale = false
                let url = try URL.init(resolvingBookmarkData: bookmarkData as Data, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &bookmarkIsStale)
                url.startAccessingSecurityScopedResource()
             catch let error as NSError 
                print("Bookmark Access Fails: \(error.description)")
            
        
    

【讨论】:

以上是关于Mac OS应用程序中用户选择的文件夹的读写权限?的主要内容,如果未能解决你的问题,请参考以下文章

mac中如何批量修改文件夹读写权限?

Mac OS 根本没有 usr/bin 文件夹的权限

Mac OS X文件系统的附加属性@如何彻底删除

mac apfs文件系统权限不能修改为读写?

Mac OS X 启用超级用户 sudo -s 获得系统权限 Mac终端命令

将“所有人”权限从“无权访问”更改为“读写”Mac