【问题标题】:Application Sandbox: renaming a file doesn't work应用程序沙箱:重命名文件不起作用
【发布时间】:2012-12-19 10:38:54
【问题描述】:

我有一个基本的 Cocoa 应用程序,让用户可以管理文件列表。文件是通过拖放添加的,我在应用程序重新启动时将访问权限保留在安全书签中。

到目前为止一切顺利。允许该应用读取和写入用户的文件,但重命名失败并出现权限错误,声称不允许我的应用访问父文件夹。

代码:

[[NSFileManager defaultManager] moveItemAtPath:currentPath 
                                        toPath:newPath error:&error]

错误:

Error Domain=NSCocoaErrorDomain Code=513 "“Some image.jpg” couldn’t 
be moved because you don’t have permission to access “some folder”

我发誓这在昨天还有效,没有任何改变...(见下面的更新)

无论如何。我假设如果用户允许通过打开对话框或拖放访问文件,则应该允许沙盒应用程序重命名文件。

更新:

我终于设法追踪到我的代码发生了变化:

当我在执行实际移动之前调用NSFileCoordinator.item(at: newSrcUrl, willMoveTo: newDstUrl) 时,我可以重现错误。如果我跳过这一步,一切都会再次运行,控制台中不会出现任何错误。

完整代码:

let coordinator = NSFileCoordinator()

coordinator.coordinate(writingItemAt: srcURL, options: .forMerging,
                       writingItemAt: dstURL, options: .forMerging) { newSrcUrl, newDstUrl in

    // Calling this first results in "could not get a sandbox extension" error in console:
    //coordinator.item(at: newSrcUrl, willMoveTo: newDstUrl)

    try moveItem(at: newSrcUrl, to: newDstUrl)

    coordinator.item(at: newSrcUrl, didMoveTo: newDstUrl)
}

item(at oldURL: URL, willMoveTo newURL: URL) 似乎是在更改文档的文件扩展名(例如 .rtf 到 .rtfd)时使用的,而我的应用程序并非如此。只有文件名改变,文件扩展名没有改变。我认为最好的做法是调用 will/didMove 方法,但看起来有副作用。

回顾一下,我的应用程序所做的只是要求用户打开一个文件夹(通过NSOpenPanel,因此我的应用程序获得了写入该文件夹的权限),然后我的应用程序在该文件夹中创建一个文件,然后重命名它.

【问题讨论】:

  • 在尝试访问该路径之前,您确定要解析安全书签吗?
  • 是的,我肯定会解析书签并开始安全访问。
  • 您能提供更多代码吗?也许足以让某人重现您的问题?现在没有太多事情要做。另外,您是否可以访问源路径和目标路径?

标签: cocoa mac-app-store appstore-sandbox


【解决方案1】:

您面临的问题不是持久化任何东西,而是沙盒的工作方式:

规则是……

重命名一个文件(实际上是执行移动操作),
您需要对所述文件的父目录具有写入权限。

我们的问题是……

如果您拖动文件,沙盒将扩展对这些文件的访问,而不是它们的父目录,因此此错误表明它需要权限

现在,尝试拖动包含这些文件的文件夹,你会发现一切正常:)

那我们该怎么办?

一个简单的解决方案是让用户通过 NSOpenPanel 选择一个“工作”目录,这样沙盒就可以访问,而不会在用户每次希望重命名时疏远他
但是我们现在却因为他一开始就不应该知道的废话而困扰用户!
所以对我来说这是糟糕的设计/用户体验

现在我已经阅读了有关沙盒的文档,我注意到 NSFileCoordinator 有一个名为 itemAtURL:willMoveToURL:

的方法

Wich 把我带到了这个小 snippet(我在这里重写了,因为它缺少 willMove 函数)
我们在这里要做的是为了重命名而请求沙箱扩展:

NSURL *sourceURL = document.fileURL;
NSURL *destinationURL = [[sourceURL URLByDeletingLastPathComponent] URLByAppendingPathComponent:fileName isDirectory:NO];

NSError *writeError = nil;
__block NSError *moveError = nil;
__block BOOL success = NO;

NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];

[coordinator coordinateWritingItemAtURL:sourceURL
                                options:NSFileCoordinatorWritingForMoving
                       writingItemAtURL:destinationURL
                                options:NSFileCoordinatorWritingForReplacing
                                  error:&writeError
                             byAccessor:^(NSURL *newURL1, NSURL *newURL2)
{
    NSFileManager *fileManager = [NSFileManager new];

    [coordinator itemAtURL:sourceURL willMoveToURL:destinationURL];

    success = [fileManager moveItemAtURL:newURL1 toURL:newURL2 error:&moveError];

    if (success)
    {
        [coordinator itemAtURL:newURL1 didMoveToURL:newURL2];
    }
}];

可悲的是,这种方法似乎只针对更改文件扩展名而不是重命名,因此日志中出现此错误:

NSFileSandboxingRequestRelatedItemExtension: an error was received from pboxd instead of a token. Domain: NSPOSIXErrorDomain, code: 1

耶,苹果,耶

【讨论】:

【解决方案2】:

以下对我有用只要我将myext 添加为我的Info.plist 中的相关项目文档类型。这在Apple's Documentation 中有说明。
摘录如下:

在这两种情况下,您都必须对应用程序的 Info.plist 文件进行少量更改。您的应用应该已经声明了一个文档类型 (CFBundleDocumentTypes) 数组,该数组声明了您的应用可以打开的文件类型。

对于该数组中的每个文件类型字典,如果该文件类型应被视为潜在相关类型以用于打开和保存目的,请添加键 NSIsRelatedItemType 和布尔值 YES。

根据 BenRhayader 的回答修改的代码:

   NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
    NSURL *sourceURL = chosenFile;
    NSURL *destinationURL = [chosenFile URLByAppendingPathExtension: @"myext"];
    
    [coordinator coordinateWritingItemAtURL:sourceURL
                                    options:NSFileCoordinatorWritingForMoving
                           writingItemAtURL:destinationURL
                                    options:NSFileCoordinatorWritingForReplacing
                                      error:NULL
                                 byAccessor:^(NSURL *newURL1, NSURL *newURL2)
    {
        NSFileManager *fileManager = [NSFileManager new];
        
        [coordinator itemAtURL:sourceURL willMoveToURL:destinationURL];
        NSError *moveError;
        BOOL success = [fileManager moveItemAtURL:newURL1 toURL:newURL2 error:&moveError];
        
        if (success)
        {
            [coordinator itemAtURL:newURL1 didMoveToURL:newURL2];
        }
    }];

【讨论】:

  • 对.. 根据 Apple 的文档,听起来您不能要求“批发”这种类型的权限,而必须声明您的应用程序将使用的文件类型。
  • 这不适用于文件夹/包。 (至少它对我不起作用)。无论如何谢谢:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-12
  • 1970-01-01
  • 2012-01-06
  • 2021-11-02
  • 1970-01-01
  • 2015-05-26
  • 1970-01-01
相关资源
最近更新 更多