MacOS沙盒应用程序:无需NSOpenPanel即可访问文件

时间:2019-01-17 16:01:29

标签: macos nsurl file-access appstore-sandbox security-scoped-bookmarks

在基于沙盒的基于NSDocument的应用程序中,无论文档保存在哪里,都可以使用NSOpenPanel访问任何兼容的文档。没有NSOpenPanel,应用程序只能访问沙箱容器中的文件。

由于我的应用程序管理两种类型的子类NSdocument(文本作为读取器/编写器,图像仅作为读取器),因此我尝试为图像实现单独的“打开最近”菜单。当用户打开它们时,我禁用了它们的常规行为,覆盖了NSDocumentController的noteNewRecentDocumentURL: (NSURL *)url方法,以为图像URL返回NO。这样,只有文本文档会出现在普通的“文件”->“打开最近的文档”菜单中(并在用户选择它们时正常打开)。图片在自定义菜单中列出。

这些图像URL出现问题,因为应用程序被沙箱化:应用程序无法直接打开专用菜单中列出的任何图像文件(任何读取操作均会返回-54错误。可以使用以下方式检查此行为:

[[NSFileManager defaultManager] isReadableFileAtPath:[fileURL path]]

在这种情况下总是返回FALSE。唯一的例外是:当我重新打开时,从专用的“打开最近的”菜单中,先前已在同一应用程序会话中使用NSOpenPanel打开了一个文件,然后将其关闭:在这种情况下,isReadableFileAtPath:返回{{1 }},可以访问该文件。但是当应用程序退出并重新启动时,无法以这种方式访问​​最近的图像文件。

我找到了解决此问题的解决方案:

  1. 一旦用户通过NSOpenPanel“合法”访问了图像文件,便将其移动到沙箱容器中。当然,它可以工作,但是会阻止用户自行决定文件的位置!同样,在沙箱中复制文件也不是解决方案。

  2. 在沙箱中为这些文件创建别名。由于找不到方法,因此无法测试这是否是解决方案。

  3. 禁用应用程序沙箱。但这是最糟糕的解决方案,因为使用沙箱的原因很多!

是否有第四个解决方案,该解决方案将授权对任何映像文件(无论位于何处)进行只读访问,而不会禁用沙箱?

2 个答案:

答案 0 :(得分:1)

无论如何都无法访问任何文件。

我也不知道您的第二个解决方案意味着什么,这可能就是您无法遵循它的原因。您可能想要引用“安全范围内的书签”,而不是“别名”,它们工作得很好,这是您应该遵循的路径。

答案 1 :(得分:1)

伊万的建议很好。经过几读(不到一个小时),我可以实现那些安全范围内的书签。对于感兴趣的人,这是主要发现。

  1. 将该功能添加到沙盒应用程序的授权文件中 将com.apple.security.files.bookmarks.document-scope(或com.apple.security.files.bookmarks.app-scope或两者)设置为TRUE。

  2. 修改文档打开方法(称为NSOpenPanel),如下所示:

-(void) openMyDocument:(id)sender{

      // ... do your stuff

    [self.panel beginWithCompletionHandler:^(NSInteger result) {
        if (result == NSModalResponseOK) {
            NSURL* selectedURL = [[self.panel URLs] objectAtIndex:0];            
            NSData *bookmark = nil;
            NSError *error = nil;
            bookmark = [selectedURL bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
                     includingResourceValuesForKeys:nil
                                      relativeToURL:nil // Make it app-scoped
                                              error:&error];
            if (error) {
                NSLog(@"Error while creating bookmark for URL (%@): %@", selectedURL, error);
            }
            NSString *access = [NSString stringWithFormat:@"%@%@", @"Access:", [selectedURL path]];
            [[NSUserDefaults standardUserDefaults] setObject:bookmark forKey:access];
            [[NSUserDefaults standardUserDefaults] synchronize];

            // ... then open the document your way
        }
    }
}
  1. 修改您创建的无需使用NSOpenPanel即可读取文件的方法
- (void) openDocumentForScopedURL: (NSURL *) fileURL

        NSString *accessKey = [NSString stringWithFormat:@"%@%@", @"Access:", [fileURL path]];
        NSData *bookmarkData = [[NSUserDefaults standardUserDefaults] objectForKey:accessKey];
        NSURL *bookmarkFileURL = nil;
        if (bookmarkData == nil){
            // no secured-scoped bookmark found, alert the user
            return;
        } else {
            NSError *error = nil;
            BOOL bookmarkDataIsStale;

            bookmarkFileURL = [NSURL
                               URLByResolvingBookmarkData:bookmarkData
                               options:NSURLBookmarkResolutionWithSecurityScope
                               relativeToURL:nil
                               bookmarkDataIsStale:&bookmarkDataIsStale
                               error:&error];
            [bookmarkFileURL startAccessingSecurityScopedResource];
        }


        // ... Then open your file, using bookmarkFileURL
        // ... and do your stuff

        // IMPORTANT. You must notify that stopped to access

        [bookmarkFileURL stopAccessingSecurityScopedResource];            
}