我是 iOS 开发的新手,所以我在这里展示和询问的一些事情可能很愚蠢,请不要生气:) 因此,我需要在我的应用程序中添加从本地存储中选取文件的支持。此功能将用于选择文件 -> 编码为 Base64,然后发送到远程服务器。现在,我在将此功能添加到我的应用程序时遇到一些问题。我找到了本教程并执行了此处提到的所有操作:
添加了导入 -import MobileCoreServices
添加实施 -UIDocumentPickerDelegate
添加了此代码范围以显示选择器:
let documentPicker = UIDocumentPickerViewController(documentTypes: [String(kUTTypeText),String(kUTTypeContent),String(kUTTypeItem),String(kUTTypeData)], in: .import)
documentPicker.delegate = self
self.present(documentPicker, animated: true)
Run Code Online (Sandbox Code Playgroud)
还添加了所选文件的处理程序:
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
print(urls)
}
Run Code Online (Sandbox Code Playgroud)
一般来说,文件选择器出现在模拟器屏幕上,但我在 XCode 中看到警告:
'init(documentTypes:in:)' was deprecated in iOS 14.0
Run Code Online (Sandbox Code Playgroud)
我访问了官方指南,在这里还发现了有关弃用某些方法的类似信息。那么,我如何才能解决我的文件选择问题,以与最新的iOS版本完全兼容。另一个问题 - 我如何对选定的文件进行编码?现在我可以选择文件并打印其位置,但我需要获取其数据,如名称、编码内容等。也许有人面临类似的问题并且知道解决方案?我需要将其添加到普通视图控制器中,因此当我尝试添加此实现时:
UIDocumentPickerViewController
Run Code Online (Sandbox Code Playgroud)
我看到这样的错误信息:
Multiple inheritance from classes 'UIViewController' and 'UIDocumentPickerViewController'
Run Code Online (Sandbox Code Playgroud)
我将非常高兴获得任何信息:教程或建议:)
一旦您有了文件 URL,您就可以使用该 URL 来检索它包含的数据。获得数据后,可以将其转换为 Base64 并将其发送到服务器。您没有提供有关如何将其发送到服务器的信息,但其余部分可能如下所示:
func sendFileWithURL(_ url: URL, completion: @escaping ((_ error: Error?) -> Void)) {
func finish(_ error: Error?) {
DispatchQueue.main.async {
completion(error)
}
}
DispatchQueue(label: "DownloadingFileData." + UUID().uuidString).async {
do {
let data: Data = try Data(contentsOf: url)
let base64String = data.base64EncodedString()
// TODO: send string to server and call the completion
finish(nil)
} catch {
finish(error)
}
}
}
Run Code Online (Sandbox Code Playgroud)
你会用它作为
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
urls.forEach { sendFileWithURL($0) { <#Your code here#> } }
}
Run Code Online (Sandbox Code Playgroud)
分解它:
要获取文件数据,您可以使用Data(contentsOf: url). 此方法甚至适用于远程文件,因此您可以使用您可以访问的互联网上任何位置的图像链接的 URL。重要的是要知道此方法将暂停您的线程,这通常不是您想要的。
为了避免破坏当前线程,我们使用创建一个新队列DispatchQueue(label: "DownloadingFileData." + UUID().uuidString)。队列的名称不是很重要,但在调试时很有用。
收到数据后,我们将其转换为 Base64 字符串data.base64EncodedString(),然后可以将该数据发送到服务器。您只需填写该TODO:部分即可。
检索文件数据可能会出现一些错误。也许访问限制或文件不再存在或没有互联网连接...这是通过抛出来处理的。如果语句因try任何原因失败,则该catch部分将执行并且您会收到错误。
由于所有这些都是在后台线程上完成的,因此返回主线程通常是有意义的。这就是该finish函数的作用。如果您不需要,只需将其删除即可:
func sendFileWithURL(_ url: URL, completion: @escaping ((_ error: Error?) -> Void)) {
DispatchQueue(label: "DownloadingFileData." + UUID().uuidString).async {
do {
let data: Data = try Data(contentsOf: url)
let base64String = data.base64EncodedString()
// TODO: send string to server and call the completion
completion(nil)
} catch {
completion(error)
}
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法还需要考虑其他事项。例如,您可以看到如果用户选择多个文件,那么每个文件都会打开自己的队列并启动该过程。这意味着,如果用户选择多个文件,则可能在某个时刻许多或全部文件将被加载到内存中。这可能会占用太多内存并使您的应用程序崩溃。您可以决定这种方法是否适合您或者您希望序列化该过程。使用队列的序列化应该非常简单。您所需要的只是拥有一个:
private lazy var fileProcessingQueue: DispatchQueue = DispatchQueue(label: "DownloadingFileData.main")
func sendFileWithURL(_ url: URL, completion: @escaping ((_ error: Error?) -> Void)) {
func finish(_ error: Error?) {
DispatchQueue.main.async {
completion(error)
}
}
fileProcessingQueue.async {
do {
let data: Data = try Data(contentsOf: url)
let base64String = data.base64EncodedString()
// TODO: send string to server and call the completion
finish(nil)
} catch {
finish(error)
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,一项操作将在另一项操作开始之前完成。但这可能仅适用于获取文件数据并转换为 Base64 字符串。如果上传是在另一个线程上完成的(通常是这样),那么您可能仍然有多个正在进行的请求,其中可能包含上传所需的所有数据。
| 归档时间: |
|
| 查看次数: |
6782 次 |
| 最近记录: |