Swift使用UIDocumentPickerController上传PDF至Firebase空文件问题求助
问题解决:UIDocumentPicker选PDF后上传Firebase为空文件
你的代码核心问题有两个:
- 没实现
UIDocumentPickerDelegate的代理方法,根本没拿到用户选中的PDF文件路径 - 上传时直接用了
let newData = Data(),传的是空数据,自然下载后也是空文件
按下面的步骤修改:
1. 添加变量存储选中的文件URL
在类里加一个可选的URL变量,用来保存用户选中的PDF路径:
var selectedDocumentURL: URL?
2. 实现文档选择器的代理方法
必须实现UIDocumentPickerDelegate的documentPicker(_:didPickDocumentsAt:)方法,才能获取用户选中的文件。另外iOS 13+需要通过文件协调器(File Coordinator)来读取文件内容,避免权限问题:
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { guard let selectedURL = urls.first else { alertFunc(titleInput: "错误", messageInput: "未选中任何文件") return } // 处理文件权限,iOS 13+需要用file coordination读取 let fileCoordinator = NSFileCoordinator() fileCoordinator.coordinate(readingItemAt: selectedURL, options: .withoutChanges, error: nil) { (readURL) in self.selectedDocumentURL = readURL self.alertFunc(titleInput: "成功", messageInput: "已选中PDF文件") } } // 处理用户取消选择的情况 func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { alertFunc(titleInput: "提示", messageInput: "已取消选择文件") }
3. 修改上传方法,读取选中的文件数据
原来的uploadClicked方法里,不要直接用空的Data(),而是从selectedDocumentURL读取数据:
@IBAction func uploadClicked(_ sender: Any) { guard let documentURL = selectedDocumentURL else { alertFunc(titleInput: "错误", messageInput: "请先选择要上传的PDF文件") return } // 从URL读取文件数据 guard let newData = try? Data(contentsOf: documentURL) else { alertFunc(titleInput: "错误", messageInput: "无法读取文件内容") return } let storage = Storage.storage() let storageReference = storage.reference() let newUuid = UUID().uuidString let mediaFolder = storageReference.child("Documents") let newDocumentReference = mediaFolder.child("\(newUuid).pdf") newDocumentReference.putData(newData, metadata: nil) { metadata, error in if let error = error { self.alertFunc(titleInput: "错误", messageInput: error.localizedDescription) return } newDocumentReference.downloadURL { url, error in guard let downloadURL = url else { self.alertFunc(titleInput: "错误", messageInput: error?.localizedDescription ?? "获取下载链接失败") return } let documentFirestore = Firestore.firestore() let firestorePost: [String: Any] = [ "documentUrl": downloadURL.absoluteString, "PostedBy": Auth.auth().currentUser?.email ?? "未知用户", "Date": FieldValue.serverTimestamp() ] documentFirestore.collection("Documents").addDocument(data: firestorePost) { error in if let error = error { self.alertFunc(titleInput: "错误", messageInput: error.localizedDescription) } else { self.tabBarController?.selectedIndex = 0 } } } } }
完整修改后的代码
import UIKit import Firebase import MobileCoreServices class DocViewController: UIViewController, UIDocumentPickerDelegate, UINavigationControllerDelegate { @IBOutlet weak var docImage: UIImageView! // 存储选中的文件URL var selectedDocumentURL: URL? override func viewDidLoad() { super.viewDidLoad() let hideKeyboardGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard)) view.addGestureRecognizer(hideKeyboardGesture) docImage.isUserInteractionEnabled = true let docGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(selectDocument)) docImage.addGestureRecognizer(docGestureRecognizer) } @objc func hideKeyboard(){ view.endEditing(true) } @objc func selectDocument(){ let documentPicker = UIDocumentPickerViewController(documentTypes: [String(kUTTypePDF)], in: .open) documentPicker.delegate = self documentPicker.modalPresentationStyle = .fullScreen documentPicker.allowsMultipleSelection = false present(documentPicker, animated: true, completion: nil) // 移除directoryURL设置,打开文档不需要指定该参数 // documentPicker.directoryURL = .documentsDirectory } func alertFunc(titleInput:String, messageInput: String){ let alert = UIAlertController(title: titleInput, message: messageInput, preferredStyle: .alert) let okButton = UIAlertAction(title: "OK", style: .default, handler: nil) alert.addAction(okButton) present(alert, animated: true, completion: nil) } // 文档选择器代理方法:获取选中的文件 func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { guard let selectedURL = urls.first else { alertFunc(titleInput: "错误", messageInput: "未选中任何文件") return } let fileCoordinator = NSFileCoordinator() fileCoordinator.coordinate(readingItemAt: selectedURL, options: .withoutChanges, error: nil) { (readURL) in self.selectedDocumentURL = readURL self.alertFunc(titleInput: "成功", messageInput: "已选中PDF文件") } } // 文档选择器取消选择 func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { alertFunc(titleInput: "提示", messageInput: "已取消选择文件") } @IBAction func uploadClicked(_ sender: Any) { guard let documentURL = selectedDocumentURL else { alertFunc(titleInput: "错误", messageInput: "请先选择要上传的PDF文件") return } guard let newData = try? Data(contentsOf: documentURL) else { alertFunc(titleInput: "错误", messageInput: "无法读取文件内容") return } let storage = Storage.storage() let storageReference = storage.reference() let newUuid = UUID().uuidString let mediaFolder = storageReference.child("Documents") let newDocumentReference = mediaFolder.child("\(newUuid).pdf") newDocumentReference.putData(newData, metadata: nil) { metadata, error in if let error = error { self.alertFunc(titleInput: "错误", messageInput: error.localizedDescription) return } newDocumentReference.downloadURL { url, error in guard let downloadURL = url else { self.alertFunc(titleInput: "错误", messageInput: error?.localizedDescription ?? "获取下载链接失败") return } let documentFirestore = Firestore.firestore() let firestorePost: [String: Any] = [ "documentUrl": downloadURL.absoluteString, "PostedBy": Auth.auth().currentUser?.email ?? "未知用户", "Date": FieldValue.serverTimestamp() ] documentFirestore.collection("Documents").addDocument(data: firestorePost) { error in if let error = error { self.alertFunc(titleInput: "错误", messageInput: error.localizedDescription) } else { self.tabBarController?.selectedIndex = 0 } } } } } }
额外细节说明:
- 移除了
documentPicker.directoryURL = .documentsDirectory,这个参数用于创建文档时指定保存目录,打开文档时无需设置,反而可能导致选择器异常 - 上传时增加了多层空值判断,避免用户未选文件就点击上传引发崩溃
- 将Firestore字段名从
imageUrl改为documentUrl,更贴合实际内容 - 处理了
Auth.auth().currentUser?.email的空值情况,避免崩溃
内容的提问来源于stack exchange,提问作者Muhammed Ömer Ünver
相关产品推荐
相关产品推荐

