我有以下 SwiftUI 代码,其中一个简单的按钮会打开 iOS 文件管理器,并允许用户选择要导入的 CSV 文件。我发现它对于本地存储在我的设备上的文件效果很好,但如果我尝试从 Google Drive 或 OneDrive 选择文件,它会获取一个 URL,但当我尝试从中检索数据时,它会返回一个错误提示找不到该文件。
\n经过一番绞尽脑汁后,我发现在使用文件浏览器时,如果我长按调出上下文菜单,然后查看文件的信息(我猜测可能会将其下拉到手机本地缓存),然后它将按预期工作。下面的 gif 动画显示了这一点:
\n\n我发现,一旦我完成了该缓存技巧,我就可以使用相同的代码在其他应用程序中访问该文件,而不会出现问题,而且我还发现我可以卸载我的应用程序并重新安装它,并且它会继续工作。
\n任何人都可以建议使用 SwiftUI 的方法,在尝试从 Google Drive 或 OneDrive 导入文件时可以避免出现“文件未找到”错误吗?
\n我用于测试的整个代码如下:
\nimport 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}\nRun Code Online (Sandbox Code Playgroud)\n控制台日志输出如下:
\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...\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\nRun Code Online (Sandbox Code Playgroud)\n我的测试是在运行 iOS 14.2 的物理 iPhone 12 Pro Max 和运行 iPadOS 14.4 的物理 iPad Air 2 上完成的。
\nLoc*_*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)