PHPickerView获取大视频URL失败:无法加载public.movie表示
尝试从PHPickerView的选择结果中获取相机胶卷内视频的URL时,仅在处理较大视频文件时出现错误,无法加载public.movie类型的表示,提示文件不存在。更换不同类型标识符也无法解决问题。
错误信息:
Error Domain=NSItemProviderErrorDomain Code=-1000 "Cannot load representation of type public.movie" UserInfo={NSLocalizedDescription=Cannot load representation of type public.movie, NSUnderlyingError=0x282932ca0 {Error Domain=NSCocoaErrorDomain Code=260 "The file couldn’t be opened because it doesn’t exist." UserInfo={NSFilePath=/private/var/mobile/Containers/Shared/AppGroup/E5CE1DB9-85C1-4378-94C2-D384E92C5268/File Provider Storage/photospicker/version=1&uuid=B42E6922-E10A-46DB-8B95-66B009F7294A&mode=compatible.mov, NSLocalizedDescription=The file couldn’t be opened because it doesn’t exist.}}}
相关代码片段:
result.itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { videoURL, error in assert(Thread.isMainThread == false) let directory = NSTemporaryDirectory() let fileName = NSUUID().uuidString.appending(".mov") if let videoURL = videoURL, let copiedURLFile = NSURL.fileURL(withPathComponents: [directory, fileName]) {
1. 强引用持有PHPickerResult,避免临时文件被提前清理
大视频的加载是异步操作,如果PHPickerResult或NSItemProvider被提前释放,系统会自动清理生成的临时文件,导致文件不存在。可以在类中声明属性持有选中结果:
private var selectedResults: [PHPickerResult] = []
在选择完成后将结果存入该数组:
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { picker.dismiss(animated: true) selectedResults = results processResult(results.first!) }
2. 使用loadInPlaceFileRepresentation替代loadFileRepresentation
loadFileRepresentation会创建文件副本,大文件复制耗时且易出问题;loadInPlaceFileRepresentation直接访问原始文件的临时链接,效率更高,也能避免副本丢失:
result.itemProvider.loadInPlaceFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { [weak self] fileURL, isInPlace, error in guard let self = self else { return } if let error = error { print(error.localizedDescription) return } guard let fileURL = fileURL else { return } // isInPlace为true时,不能修改原文件,需复制到自定义目录再操作 if isInPlace { let tempDir = FileManager.default.temporaryDirectory let targetURL = tempDir.appendingPathComponent("\(NSUUID().uuidString).mov") do { try FileManager.default.copyItem(at: fileURL, to: targetURL) // 后续使用targetURL处理视频 } catch { print("复制文件失败:\(error)") } } else { // 非原地文件可直接操作fileURL } }
3. 修正文件复制路径逻辑
避免手动拼接路径导致的错误,用FileManager提供的方法生成合法路径:
let tempDir = FileManager.default.temporaryDirectory let targetURL = tempDir.appendingPathComponent("\(NSUUID().uuidString).mov") do { try FileManager.default.copyItem(at: videoURL, to: targetURL) // 使用targetURL进行后续处理 } catch { print("复制文件失败:\(error)") }
4. 注意线程切换
异步回调默认在后台线程,如果后续需要更新UI,必须切回主线程,避免UI操作异常。
内容的提问来源于stack exchange,提问作者EAO123

