UIDocumentPickerViewController 返回不存在的文件的 url



我正在使用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(来重现这一点。然后关闭了无线网络。然后我尝试在我的应用程序中选择它们:

同步的文件:

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"}}

说明

类似的问题也发生在我身上。我像这样初始化了文档选择器:

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

这意味着在documentPicker中选择文件后,文件将复制到app_id-Inbox目录中。当调用委托方法documentPicker(_:didPickDocumentsAt:)时,它会给出

指向位于目录中的文件app_id-Inbox URL。

问题

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

这似乎是一个错误,因为Apple的文档在此处指出

UIDocumentPickerModeImport

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

它清楚地表明直到应用程序终止,但就我而言,大约是一分钟没有打开该 URL。

解决方法

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

斯威夫特 4

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    let newUrls = urls.compactMap { (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目录,但建议在不再需要时清除其内容。

我不

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

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

在文档选择器视图控制器的导入模式下,我用这个解决它(抱歉,我将粘贴 C# 代码而不是 swift,因为我面前有它(

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

NSData fileData = NSData.FromUrl(url);

现在你有包含文件数据的"文件数据"变量。

然后,您可以将它们复制到应用程序隔离空间中的自定义文件夹中,并且工作正常。

使用文档选取器时,用于使用文件系统将URL转换为文件路径,如下所示:

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

我遇到了类似的问题。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和模拟器,文件位于"文件提供程序存储"文件夹下,对于iPhone文件,文件将位于"文档"文件夹下。使用此路径,您还可以获取文件的扩展名和名称。

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

以下是用 Swift 5 编写的完整代码,用于支持早期版本的 iOS 14 及更高版本

此方法已从 iOS 14 中弃用

public init(documentTypes allowedUTIs: [String], in mode: UIDocumentPickerMode)
  1. 在按钮操作中编写此代码

    @IBAction func importItemFromFiles(sender: UIBarButtonItem) {
             var documentPicker: UIDocumentPickerViewController!
             if #available(iOS 14, *) {
                 // iOS 14 & later
                 let supportedTypes: [UTType] = [UTType.image]
                 documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: supportedTypes)
             } else {
                 // iOS 13 or older code
                 let supportedTypes: [String] = [kUTTypeImage as String]
                 documentPicker = UIDocumentPickerViewController(documentTypes: supportedTypes, in: .import)
             }
             documentPicker.delegate = self
             documentPicker.allowsMultipleSelection = true
             documentPicker.modalPresentationStyle = .formSheet
             self.present(documentPicker, animated: true)
         }
    
  2. 实现委托

标记: - UIDocumentPickerDelegate Methods

extension MyViewController: UIDocumentPickerDelegate {
   func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    for url in urls {
        
        // Start accessing a security-scoped resource.
        guard url.startAccessingSecurityScopedResource() else {
            // Handle the failure here.
            return
        }
        
        do {
            let data = try Data.init(contentsOf: url)
            // You will have data of the selected file
        }
        catch {
            print(error.localizedDescription)
        }
        
        // Make sure you release the security-scoped resource when you finish.
        defer { url.stopAccessingSecurityScopedResource() }
    }        
  }
   func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
      controller.dismiss(animated: true, completion: nil)
  }
}

我可以添加一些有趣的东西。

重复导入同一文件时,也会发生此问题。我在本地文件系统上遇到了一个非常小的文件(~900字节(的问题。

iOS 将在将文件放入收件箱并通知代理人后正好 60 秒删除该文件。如果您再次导入具有相同名称(或最后一个路径组件(的文件,这些删除操作似乎可能会排队并在 60 秒后开始造成严重破坏。这在 iOS 13(已弃用的初始值设定项(和 14(新的初始值设定项(上都会发生。

我尝试了一个经过良好测试的代码片段来执行协调删除,但它没有帮助。

这个问题非常活跃,对导入功能有很大的损害;我不认为用例非常罕见或古怪。尽管显然没有很多关于此的帖子 - 也许每个人都只是从导入切换到打开(即使如果你想成为好公民,它要求协调文件操作(?

相关内容

  • 没有找到相关文章

最新更新