【问题标题】:Handling application: openURL: sourceApplication: to open files in iOS app处理应用程序:openURL:sourceApplication: 在 iOS 应用程序中打开文件
【发布时间】:2018-11-29 11:07:48
【问题描述】:

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

直到最近一直运行良好,某些应用程序,例如 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 等的信息,但它不适用于我正在开发的应用程序类型,因为它不能在单个“元素”上运行,我提到的大文件只是其中之一可能的来源以及我所看到的关于就地开放的内容不适用于我的案例。

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

干杯!

【问题讨论】:

  • 我遇到了同样的问题,我解决了这个问题:stackoverflow.com/questions/47430244/…
  • 是的,对不起,就是这样,我就此事联系了 Apple,忘记留下回复以供将来参考,我会贴出我最终得到的代码并关闭它。

标签: ios objective-c file-io ios11 icloud


【解决方案1】:

我就此事联系了 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),因为调用 stop 失败会导致问题,因此复制方法效果很好,特别是当这些文件将被内存映射时。 (我的情况,因为它们很大)

【讨论】:

  • startAccessingSecurityScopedResource 返回 false。从文件应用程序中选择的文件指向 tmp 文件夹,但从邮箱中选择文件后会自动创建收件箱文件夹。你能在这件事上取悦我吗?
【解决方案2】:

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];

【讨论】:

    猜你喜欢
    • 2018-11-19
    • 1970-01-01
    • 1970-01-01
    • 2013-04-27
    • 2016-10-04
    • 1970-01-01
    • 2017-04-20
    • 1970-01-01
    • 2018-07-28
    相关资源
    最近更新 更多