首页 > 解决方案 > 处理应用程序:openURL:sourceApplication: 在 iOS 应用程序中打开文件

问题描述

我有一个应用程序,它使用一些已知的大文件格式,并且从 iOS4 天开始就支持 iOS 的“打开方式...”功能。

直到最近一直运行良好,某些应用程序,如 iOS 内置邮件应用程序将通过在我的应用程序内的 ~/Documents/Inbox 目录中复制来打开该应用程序,并且生活很好。

我最近意识到“打开方式...”不再适用于我的应用程序,至少当文件通过 iOS11+ 内置文件应用程序来自 iCloud/Dropbox/GoogleDrive 时,我想知道如果我缺少任何东西。

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

这是我的代码:

- (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

正如您所看到的,两个正常工作的文件都将它们的数据复制到了设备中我的本地应用程序目录中。

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

我已经阅读了一些关于 open in place 等的内容,但它不适用于我正在开发的应用程序类型,因为它不能在单个“元素”上运行,我提到的大文件只是可能的来源之一因此,我所看到的关于就地开放的事情并不适用于我的案例。

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

干杯!

标签: iosobjective-cfile-ioios11icloud

解决方案


我联系了苹果公司,因为它没有多大意义,文档提到了使用的方法,但你真的需要深入挖掘,这并不完全明显,因为它在另一个不合逻辑的地方提到。

事实证明,您在 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],重要的是要注意它们必须成对调用(开始/停止),因为调用停止失败会导致问题,因此复制方法效果很好,特别是当那些文件将被内存映射。(我的情况,因为它们很大)


推荐阅读