PHPickerViewController视频上传进度追踪及长视频上传提速问题
解决方案:大视频加载进度追踪与提速优化
一、追踪视频加载进度
PHPickerViewController的loadFileRepresentation方法没有内置进度回调,要实现进度追踪,需通过PHAsset结合PHAssetResourceManager处理,该方案支持实时获取加载进度:
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { picker.dismiss(animated: true) progressWheel.startAnimating() progressWheel.alpha = 1 for result in results { guard let assetID = result.assetIdentifier else { continue } let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [assetID], options: nil) guard let asset = fetchResult.firstObject else { continue } let resources = PHAssetResource.assetResources(for: asset) guard let videoResource = resources.first(where: { $0.type == .video }) else { continue } let tempPath = NSTemporaryDirectory().appending(NSUUID().uuidString).appending(".mov") let tempFileURL = URL(fileURLWithPath: tempPath) let requestOptions = PHAssetResourceRequestOptions() // 实时回调加载进度 requestOptions.progressHandler = { progress, _, _, _ in DispatchQueue.main.async { self.progressWheel.progress = Float(progress) } } PHAssetResourceManager.default().requestDataForAssetResource(videoResource, options: requestOptions) { data in // 分块写入临时文件 if let fileHandle = try? FileHandle(forWritingTo: tempFileURL) { fileHandle.seekToEndOfFile() fileHandle.write(data) fileHandle.closeFile() } } completionHandler: { error in DispatchQueue.main.async { self.progressWheel.stopAnimating() } guard error == nil else { print("视频加载失败:\(error!)") return } // 加载完成,执行上传 self.uploadVideo(videoURL: tempFileURL) } } }
二、提速优化方案
1. 移除冗余操作与不必要的文件复制
你的代码中重复调用了两次loadFileRepresentation,这会导致重复加载视频,直接删除其中一次即可。另外,若videoURL本身是可直接访问的本地文件(绝大多数场景下都是),可直接传入Firebase上传,跳过临时文件复制步骤,减少IO开销:
result.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { videoURL, error in guard let videoURL = videoURL, error == nil else { print(error!) return } DispatchQueue.main.async { self.uploadVideo(videoURL: videoURL) self.progressWheel.stopAnimating() } }
2. 直接用Firebase Storage的PHAsset上传API
Firebase Storage支持直接从PHAsset上传,跳过本地文件下载/复制环节,大幅提升大视频处理效率:
import FirebaseStorage func uploadAssetToFirebase(asset: PHAsset) { let storageRef = Storage.storage().reference().child("videos/\(NSUUID().uuidString).mov") let metadata = StorageMetadata() metadata.contentType = "video/quicktime" storageRef.putFile(from: asset, metadata: metadata) { metadata, error in guard error == nil else { print("上传失败:\(error!)") return } // 获取下载URL storageRef.downloadURL { url, error in guard let downloadURL = url else { return } print("上传完成:\(downloadURL)") } } }
调用时直接传入之前获取到的PHAsset即可,Firebase会自动处理资源加载,无需手动操作文件。
3. 后台任务保障大文件操作
对于超5分钟的视频,建议将加载和上传放到后台队列执行,同时启动后台任务,避免APP进入后台后被系统终止:
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid // 开始操作前启动后台任务 backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "VideoUpload") { UIApplication.shared.endBackgroundTask(self.backgroundTaskID) self.backgroundTaskID = .invalid } // 操作完成后结束后台任务 DispatchQueue.main.async { UIApplication.shared.endBackgroundTask(self.backgroundTaskID) self.backgroundTaskID = .invalid }
4. 修复错误处理隐患
代码中使用try!会导致复制失败时直接崩溃,替换为do-catch安全处理:
do { try FileManager.default.copyItem(at: videoURL, to: copiedURLFile) } catch { print("文件复制失败:\(error)") return }
内容的提问来源于stack exchange,提问作者EAO123
相关产品推荐
相关产品推荐

