处理应用程序:openURL: sourceApplication: 在 iOS 应用程序中打开文件

Handling application: openURL: sourceApplication: to open files in iOS app

我有一个应用程序使用一些已知的大文件格式,并且自 iOS4 天以来就支持 iOS 的 "Open In..." 功能。

直到最近工作正常,某些应用程序,如 iOS 内置邮件应用程序会通过在我的应用程序内的 ~/Documents/Inbox 目录中制作副本来打开应用程序,生活是不错

我最近得知 "Open In..." 不再适用于我的应用程序,至少当文件来自 iCloud/Dropbox/GoogleDrive 通过 iOS11+ 内置-在“文件”应用中,我想知道我是否遗漏了什么。

检查代码后,我开始认为我一定遗漏了一些非常明显的东西,因为代码似乎是正确的 not 打开文件,因为给定的路径是要么无效,要么我的应用没有读取它们的必要权限。

这是我的代码:

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)urlToOpen sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    if([urlToOpen isFileURL])  // <<< this goes through
    {
        if([[NSFileManager defaultManager] isReadableFileAtPath:urlToOpen.path])
        {
            // <<< this doesn't
        }
   }
}

示例工作路径:

/private/var/mobile/Containers/Data/Application/F99A3EA4-457C-4043-AEB3-A9D961184360/Documents/plane.obj
/private/var/mobile/Containers/Data/Application/F99A3EA4-457C-4043-AEB3-A9D961184360/Documents/Inbox/cube.obj

无效的路径示例:

/private/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/somedir/somefile.ext
/private/var/mobile/Containers/Shared/AppGroup/A7FA40A0-2C7D-4FC1-BF21-A8E88B106FF9/File Provider Storage/7791941/local-storage/L3dzX3JhZmFfemFiYWxhL2Jhc2VvcmNfbWVzaC5vYmo=/somefile.ext

如您所见,两个有效的文件都已将其数据复制到设备中我的本地应用程序目录中。

如何打开这些文件?请注意,我提到该应用程序使用大文件,因为它确实如此,所以我无法获取它们的内存副本,这没有多大帮助,我需要获取文件路径以便进行内存映射并在正确的时间加载需要的内容。

我已经阅读了一些关于就地打开等内容,但它不适用于我正在开发的应用程序类型,因为它不能在单个 "element" 我提到的大文件上运行只是可能的来源之一,因此我所看到的关于就地开放的内容不适用于我的情况。

有没有办法让应用程序能够直接访问这些文件,或者至少将它们复制到我的应用程序沙盒临时目录中,以便我可以直接对它们进行内存映射?

干杯!

我就此联系了 Apple,因为它没有多大意义,文档提到了使用方法,但你确实需要深入挖掘,而且它并不完全明显,因为它在不同的非逻辑位置提到。

事实证明,您在 AppDelegate 中提供的 NSURL 是受保护的,您最安全的选择是阅读它们或将它们复制到您的本地驱动器。

我最终得到了这个:

NSString* urlPath = url.path;
if(![[NSFileManager defaultManager] isReadableFileAtPath:urlPath])
{
    if([url startAccessingSecurityScopedResource])
    {
        NSString* docsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString* destPath = [NSString stringWithFormat:@"%@/%@", docsPath, [url.path lastPathComponent]];
        urlPath = [FileHandler copyFileAtPath:url.path toPath:destPath increment:YES];
        [url stopAccessingSecurityScopedResource];
    }
}

其中 copyFileAtPath:toPath:increment 是一个自定义函数,它简单地复制文件并在文件存在时递增它们以避免覆盖,但实际上可以是任何东西。

这里的关键方法是 [NSURL startAccessingSecurityScopedResource] 和 [NSURL stopAccessingSecurityScopedResource],重要的是要注意它们必须成对调用 (start/stop) 因为调用停止失败会导致问题,因此复制方法效果很好,特别是当这些文件将被内存映射时。 (我的箱子很大)

Exaberri Tokugawa 的回答帮助我找到了这个解决方案而无需复制文件:

NSString* urlPath = url.path;
NSData *data = nil;
if(![[NSFileManager defaultManager] isReadableFileAtPath:urlPath])
{
    if([url startAccessingSecurityScopedResource])
    {
        data = [NSData dataWithContentsOfFile:urlPath];
        [url stopAccessingSecurityScopedResource];
    }
}
else data = [NSData dataWithContentsOfFile:urlPath];