UIDocumentPickerViewController将url返回给不存在的文件

时间:2016-05-09 06:32:44

标签: ios icloud icloud-drive uidocumentpickervc

我使用UIDocumentPickerViewController让用户从iCloud Drive中选择一个文件上传到后端。

大部分时间,它都能正常工作。但是,有时(特别是当互联网连接不稳定时)documentPicker:didPickDocumentAtURL:会给出一个实际上不存在于文件系统上的URL,并且任何使用它的尝试都会返回NSError"没有这样的文件或目录"

处理此问题的正确方法是什么?我正在考虑使用NSFileManager fileExistsAtPath:并告诉用户如果不存在则再试一次。但这并非听起来非常友好。有没有办法从iCloud Drive获得真正的错误原因,也许告诉iCloud Drive再试一次?

代码的相关部分:

@IBAction func add(sender: UIBarButtonItem) {
    let documentMenu = UIDocumentMenuViewController(
        documentTypes: [kUTTypeImage as String],
        inMode: .Import)

    documentMenu.delegate = self
    documentMenu.popoverPresentationController?.barButtonItem = sender
    presentViewController(documentMenu, animated: true, completion: nil)
}

func documentMenu(documentMenu: UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController) {
    documentPicker.delegate = self
    documentPicker.popoverPresentationController?.sourceView = self.view
    presentViewController(documentPicker, animated: true, completion: nil)
}

func documentPicker(controller: UIDocumentPickerViewController, didPickDocumentAtURL url: NSURL) {
    print("original URL", url)

    url.startAccessingSecurityScopedResource()

    var error: NSError?
    NSFileCoordinator().coordinateReadingItemAtURL(
    url, options: .ForUploading, error: &error) { url in
        print("coordinated URL", url)
    }

    if let error = error {
        print(error)
    }

    url.stopAccessingSecurityScopedResource()
}

我通过向OS X上的iCloud Drive添加两个大图像(每个~5MiB)并在iPhone上只打开其中一个(a synced file.bmp)而不打开另一个(an unsynced file.bmp)来重现这一点。然后关掉WiFi。然后我尝试在我的应用程序中选择它们:

同步文件:

original URL file:///private/var/mobile/Containers/Data/Application/CE70EE57-B906-4BF8-B351-A57110BE2B01/tmp/example.com.demo-Inbox/a%20synced%20file.bmp
coordinated URL file:///private/var/mobile/Containers/Data/Application/CE70EE57-B906-4BF8-B351-A57110BE2B01/tmp/CoordinatedZipFileDR7e5I/a%20synced%20file.bmp

未同步的文件:

original URL file:///private/var/mobile/Containers/Data/Application/CE70EE57-B906-4BF8-B351-A57110BE2B01/tmp/example.com.demo-Inbox/an%20unsynced%20file.bmp
Error Domain=NSCocoaErrorDomain Code=260 "The file “an unsynced file.bmp” couldn’t be opened because there is no such file." UserInfo={NSURL=file:///private/var/mobile/Containers/Data/Application/CE70EE57-B906-4BF8-B351-A57110BE2B01/tmp/example.com.demo-Inbox/an%20unsynced%20file.bmp, NSFilePath=/private/var/mobile/Containers/Data/Application/CE70EE57-B906-4BF8-B351-A57110BE2B01/tmp/example.com.demo-Inbox/an unsynced file.bmp, NSUnderlyingError=0x15fee1210 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}

6 个答案:

答案 0 :(得分:11)

描述

我遇到了类似的问题。 我有文件选择器初始化如下:

var documentPicker: UIDocumentPickerViewController = UIDocumentPickerViewController(documentTypes: ["public.data"], in: .import)

这意味着在app_id-Inbox中选择文件后,文件将被复制到documentPicker目录。当调用委托方法documentPicker(_:didPickDocumentsAt:)时,它会提供指向位于app_id-Inbox目录中的文件的URL。

问题

一段时间后(没有关闭应用程序)这些URL指向不存在的文件。发生这种情况是因为同时清除了app_id-Inbox文件夹中的tmp/。例如,我选择文档,在表格视图中显示它们并将iPhone保留在该屏幕上一分钟,然后当我尝试使用QLPreviewController提供的URL点击在documentPicker中打开文件的特定文档时返回文件不存在。

这似乎是一个错误,因为Apple的文档说明了here

  

UIDocumentPickerModeImport

     

URL指的是所选文档的副本。这些文件   是临时文件。它们仅在您的申请之前可用   终止。要保留永久副本,请将这些文件移动到永久文件   沙箱内的位置。

它明确地说直到应用程序终止,但在我的情况下,这是在不打开该URL的那一刻。

解决方法

将文件从app_id-Inbox文件夹移至tmp/或任何其他目录,然后使用指向新位置的网址。

Swift 4

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    let newUrls = urls.flatMap { (url: URL) -> URL? in
        // Create file URL to temporary folder
        var tempURL = URL(fileURLWithPath: NSTemporaryDirectory())
        // Apend filename (name+extension) to URL
        tempURL.appendPathComponent(url.lastPathComponent)
        do {
            // If file with same name exists remove it (replace file with new one)
            if FileManager.default.fileExists(atPath: tempURL.path) {
                try FileManager.default.removeItem(atPath: tempURL.path)
            }
            // Move file from app_id-Inbox to tmp/filename
            try FileManager.default.moveItem(atPath: url.path, toPath: tempURL.path)
            return tempURL
        } catch {
            print(error.localizedDescription)
            return nil
        }
    }
    // ... do something with URLs
}

虽然系统会处理/tmp目录,但建议在不再需要时清除其内容。

答案 1 :(得分:1)

我遇到了同样的问题,但事实证明我在视图上删除了Temp目录。因此它会在出现时保存然后删除并正确调用documentPicker:didPickDocumentAtURL:只有url指向我删除的文件。

答案 2 :(得分:0)

我认为问题不在于tmp目录是以某种方式清理的。

如果您使用模拟器并从icloud打开文件,您将看到该文件存在于app-id-Inbox中而不是清除/删除。因此,当您尝试读取文件或复制并获取该文件不存在的错误时,我认为这是一个安全问题,因为您可以看到该文件仍然存在。

在DocumentPickerViewController的导入模式下,我用它来解决它 (对不起,我会粘贴c#代码而不是快速,因为我已经把它放在我面前)

在DidPickDocument方法中返回我做过的NSUrl

NSData fileData = NSData.FromUrl(url);

现在您拥有了包含文件数据的“fileData”变量。

然后你可以在app的自定义文件夹中将它们复制到app的隔离空间中,它可以正常工作。

答案 3 :(得分:0)

在使用文档选择器时,用于使用文件系统 确实将网址转换为文件路径,如下所示:

var filePath = urls[0].absoluteString
filePath = filePath.replacingOccurrences(of: "file:/", with: "")//making url to file path

答案 4 :(得分:0)

在使用文档选择器时,对于使用文件系统,请按照以下步骤将url转换为文件路径:

var filePath = urls[0].absoluteString
filePath = filePath.replacingOccurrences(of: "file:/", with: "")//making url to file path

答案 5 :(得分:0)

我遇到了类似的问题。一段时间后,url 路径中的文件被删除。 我通过使用 .open UIDocumentPickerMode 而不是 .import 解决了。

let importMenu = UIDocumentPickerViewController(documentTypes: [String(kUTTypeData)], in: .open)

importMenu.delegate = self

importMenu.modalPresentationStyle = .formSheet

present(importMenu, animated: true, completion: nil)

在这种情况下,所选文档的 url 路径将在以下委托方法中更改。

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {

    print("url documentPicker:",urls)

}

您现在可以观察到路径已更改。现在我们得到了文件所在的确切路径。所以一段时间后它不会被删除。 iPad 和模拟器文件位于“File Provider Storage”文件夹下,iPhone 文件位于“Document”文件夹下。使用此路径,您还可以获得文件的扩展名和名称。