macos - MacOS 沙盒应用程序:无需 NSOpenPanel 即可访问文件
问题描述
在沙盒化的基于 NSDocument 的应用程序中,可以使用 NSOpenPanel 访问任何兼容的文档,无论文档保存在何处。如果没有 NSOpenPanel,应用程序只能访问沙盒容器中的文件。
由于我的应用程序管理两种类型的子类 NSdocument(文本作为读取器/写入器和图像仅作为读取器),我尝试为图像实现一个单独的“打开最近”菜单。当用户打开它们时,我禁用了它们的普通行为,覆盖了noteNewRecentDocumentURL: (NSURL *)url
NSDocumentController 的方法以返回图像 url 的 NO。这样只有文本文档出现在普通的文件 -> 打开最近的菜单中(当用户选择它们时正常打开)。图像列在自定义菜单中。
这些图像 url 出现问题,因为应用程序是沙盒化的:应用程序无法直接打开专用菜单中列出的任何图像文件(任何读取操作都会返回 -54 错误。可以使用以下方法检查此行为:
[[NSFileManager defaultManager] isReadableFileAtPath:[fileURL path]]
FALSE
在这种情况下总是返回。只有一个例外:当我从专用的“打开最近”菜单重新打开一个文件时,该文件之前已在同一应用程序会话中使用 NSOpenPanel 打开,然后关闭:在这种情况下isReadableFileAtPath:
返回TRUE
并且可以访问该文件。但是当应用程序退出并重新启动时,不能以这种方式访问最近的图像文件。
我确定了解决此问题的三个解决方案:
一旦用户“合法”地通过 NSOpenPanel 访问图像文件,就将其移动到沙盒容器中。当然,它可以工作,但会阻止用户自行决定文件的位置!同样,在沙箱中复制文件也不是解决办法。
在沙箱中为这些文件创建别名。由于我找不到这样做的方法,我无法测试这是否是一个解决方案。
禁用应用程序沙盒。但这是更糟糕的解决方案,因为使用沙盒的原因有很多!
是否有第四个解决方案,它可以授权对任何图像文件的只读访问,无论它位于何处,而不禁用沙箱?
解决方案
嗯,伊万的建议非常好。经过几次阅读(不到一个小时),我可以实现那些安全范围的书签。对于感兴趣的人,这里是主要发现。
将该功能添加到沙盒应用程序的权利文件中,将 com.apple.security.files.bookmarks.document-scope(或 com.apple.security.files.bookmarks.app-scope,或两者)键设置为 TRUE。
像这样修改您的文档打开方法(调用 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
}
}
}
- 修改您创建的方法以在不使用 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];
}
推荐阅读
- excel - 应用程序定义或对象定义错误 (1004)
- python - Pygame精灵二段跳
- rdf - 如何知道 RDF 图是否精益?
- three.js - 如何在 ThreeJS 中使用 InstancedBufferGeometry 进行光线投射?
- python - 连接时保持 altair 滑块和绘图
- mongodb - macOS Catalina 上的 Mongodb 仍在使用 /data/db,尽管在配置文件中指定了新路径
- pandas - 删除列中没有浮动值的行
- java - 正则表达式 - 如何仅匹配具有 <111>a 后跟任意数量字符但不应具有 context_reg="gamma" 的行
- linux - /etc/profile 中的变量测试
- angular - 在Angular 8中手动将选择字段设置为无效