SWIFTUI - 尝试从 OneDrive 和 GoogleDrive 等云文件提供商导入文件时出现“文件未找到”错误

Loc*_*ter 7 ios swift swiftui

我有以下 SwiftUI 代码,其中一个简单的按钮会打开 iOS 文件管理器,并允许用户选择要导入的 CSV 文件。我发现它对于本地存储在我的设备上的文件效果很好,但如果我尝试从 Google Drive 或 OneDrive 选择文件,它会获取一个 URL,但当我尝试从中检索数据时,它会返回一个错误提示找不到该文件。

\n

经过一番绞尽脑汁后,我发现在使用文件浏览器时,如果我长按调出上下文菜单,然后查看文件的信息(我猜测可能会将其下拉到手机本地缓存),然后它将按预期工作。下面的 gif 动画显示了这一点:

\n

SwiftUI - 尝试从 Google 云端硬盘导入时找不到文件

\n

我发现,一旦我完成了该缓存技巧,我就可以使用相同的代码在其他应用程序中访问该文件,而不会出现问题,而且我还发现我可以卸载我的应用程序并重新安装它,并且它会继续工作。

\n

任何人都可以建议使用 SwiftUI 的方法,在尝试从 Google Drive 或 OneDrive 导入文件时可以避免出现“文件未找到”错误吗?

\n

我用于测试的整个代码如下:

\n
import SwiftUI\n\nstruct ContentView: View {\n    \n    @State private var isImporting: Bool = false\n    @State private var fileContentString = ""\n    @State var alertMsg = ""\n    @State var showAlert = false\n    \n    func reportError(error: String) {\n        alertMsg =  error\n        showAlert.toggle()\n    }\n    \n    var body: some View {\n        \n        VStack {\n            Button(action: { isImporting = true}, label: {\n                Text("Select CSV File")\n            })\n            .padding()\n            \n            Text(fileContentString) //This will display the imported CSV as text in the view.\n        }\n        .padding()\n        .fileImporter(\n            isPresented: $isImporting,\n            allowedContentTypes: [.commaSeparatedText],\n            allowsMultipleSelection: false\n        ) { result in\n            do {\n                guard let selectedFileURL: URL = try result.get().first else {\n                    alertMsg = "ERROR: Result.get() failed"\n                    self.reportError(error: alertMsg)\n                    return\n                    \n                }\n                print("selectedFileURL is \\(selectedFileURL)")\n                \n                if selectedFileURL.startAccessingSecurityScopedResource() {\n                    //print("startAccessingSecurityScopedResource passed")\n                    \n                    do {\n                        print("Getting Data from URL...")\n                        let inputData = try Data(contentsOf: selectedFileURL)\n                        \n                        print("Converting data to string...")\n                        let inputString = String(decoding: inputData, as: UTF8.self)\n                        \n                        print(inputString)\n                        \n                        fileContentString = inputString\n                        \n                    }\n                    catch {\n                        alertMsg = "ERROR: \\(error.localizedDescription)"\n                        self.reportError(error: alertMsg)\n                        print(alertMsg)\n                    }\n                    \n                    //defer { selectedFileURL.stopAccessingSecurityScopedResource() }\n                    \n                } else {\n                    // Handle denied access\n                    alertMsg = "ERROR: Unable to read file contents - Access Denied"\n                    self.reportError(error: alertMsg)\n                    print(alertMsg)\n                }\n            } catch {\n                // Handle failure.\n                alertMsg = "ERROR: Unable to read file contents - \\(error.localizedDescription)"\n                self.reportError(error: alertMsg)\n                print(alertMsg)\n            }\n        }\n        .alert(isPresented: $showAlert, content: {\n            Alert(title: Text("Message"), message: Text(alertMsg), dismissButton: .destructive(Text("OK"), action: {\n                \n            }))\n        })\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

控制台日志输出如下:

\n
selectedFileURL is file:///private/var/mobile/Containers/Shared/AppGroup/8F147702-8630-423B-9DA0-AE49667748EB/File%20Provider%20Storage/84645546/1aTSCPGxY3HzILlCIFlMRtx4eEWDZ2JAq/example4.csv\nGetting Data from URL...\nERROR: The file \xe2\x80\x9cexample4.csv\xe2\x80\x9d couldn\xe2\x80\x99t be opened because there is no such file.\nselectedFileURL is file:///private/var/mobile/Containers/Shared/AppGroup/8F147702-8630-423B-9DA0-AE49667748EB/File%20Provider%20Storage/84645546/1aTSCPGxY3HzILlCIFlMRtx4eEWDZ2JAq/example4.csv\nGetting Data from URL...\nConverting data to string...\nFirst Name,Last Name\nLuke,Skywalker\nDarth,Vader\n
Run Code Online (Sandbox Code Playgroud)\n

我的测试是在运行 iOS 14.2 的物理 iPhone 12 Pro Max 和运行 iPadOS 14.4 的物理 iPad Air 2 上完成的。

\n

Loc*_*ter 10

我找到了我的问题的答案。解决方案是使用 NSFileCoordinator() 强制下载文件。

使用下面的代码,如果我访问云存储中之前未下载到本地设备的文件,它将打印“文件不可用”,但现在它只会下载该文件,而不是抛出文件未找到错误。

理想情况下,我希望能够首先下载文件属性元数据以检查文件有多大,然后决定是否要下载完整文件。NSFileCoordinator 有一个仅元数据选项,但我还没有弄清楚如何检索和解释其中的结果。暂时这样就可以了...

if selectedFileURL.startAccessingSecurityScopedResource() {
    let fileManager = FileManager.default
    if fileManager.fileExists(atPath: selectedFileURL.path) {
        print("FILE AVAILABLE")
    } else {
        print("FILE NOT AVAILABLE")
    }
    
    var error: NSError?
    
    NSFileCoordinator().coordinate(
        readingItemAt: selectedFileURL, options: .forUploading, error: &error) { url in
        print("coordinated URL", url)
        let coordinatedURL = url
        
        isShowingFileDetails = false
        importedFileURL = selectedFileURL
        
        do {
            let resources = try selectedFileURL.resourceValues(forKeys:[.fileSizeKey])
            let fileSize = resources.fileSize!
            print ("File Size is \(fileSize)")
        } catch {
            print("Error: \(error)")
        }
    }
    
    do {
        print("Getting Data from URL...")
        let inputData = try Data(contentsOf: selectedFileURL)
        print("Do stuff with file....")
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 非常好,尼斯湖水怪。我多年来一直在解决这个问题,而您已经找到了解决方案(或者说是解决方法)。谢谢。 (2认同)