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

Posted

技术标签:

【中文标题】SWIFTUI - 尝试从 OneDrive 和 GoogleDrive 等云文件提供商导入文件时出现文件未找到错误【英文标题】:SWIFTUI - File Not Found error when trying to import a file from a cloud file provider like OneDrive and GoogleDrive 【发布时间】:2021-04-23 14:26:30 【问题描述】:

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

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

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

谁能建议使用 SwiftUI 的方法,在尝试从 Google Drive 或 OneDrive 导入文件时,我可以避免此 File Not Found 错误?

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

import SwiftUI

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

控制台日志输出如下:

selectedFileURL is file:///private/var/mobile/Containers/Shared/AppGroup/8F147702-8630-423B-9DA0-AE49667748EB/File%20Provider%20Storage/84645546/1aTSCPGxY3HzILlCIFlMRtx4eEWDZ2JAq/example4.csv
Getting Data from URL...
ERROR: The file “example4.csv” couldn’t be opened because there is no such file.
selectedFileURL is file:///private/var/mobile/Containers/Shared/AppGroup/8F147702-8630-423B-9DA0-AE49667748EB/File%20Provider%20Storage/84645546/1aTSCPGxY3HzILlCIFlMRtx4eEWDZ2JAq/example4.csv
Getting Data from URL...
Converting data to string...
First Name,Last Name
Luke,Skywalker
Darth,Vader

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

【问题讨论】:

【参考方案1】:

我找到了问题的答案。解决方案是使用 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....")
    

【讨论】:

非常好,尼斯湖水怪。多年来,我一直在为这个问题苦苦挣扎,而您已经找到了解决方案(或者说是一种解决方法)。谢谢。 非常感谢@LochNessMonster!我在这个问题上花了 3 天的时间......你解决了它!

以上是关于SWIFTUI - 尝试从 OneDrive 和 GoogleDrive 等云文件提供商导入文件时出现文件未找到错误的主要内容,如果未能解决你的问题,请参考以下文章

rClone 安装 Onedrive 业务

使用 Python 在 OneDrive 文件夹中打开 excel 工作簿

尝试使用环境变量从 HostingViewController 呈现 SwiftUI

Evernote 和 OneDrive 的文件选择器 API

OneDrive - “You‘re already syncing this account.“ 错误的解决方案

关于发现和授权的 OneDrive for Business API 问题