SwiftUI如何获取本地视频路径以上传至Firebase Storage?
问题描述
我想把视频上传到Firebase Storage,上传代码需要先获取本地视频URL:
let localFile = URL(string: "path/to/video")!
然后用这段代码完成上传:
let uploadTask = riversRef.putFile(from: localFile)
现在的问题是不知道怎么获取这个视频路径。我已经做了一个自定义的图片/视频选择器,图片上传功能正常,但视频上传卡在这里,核心就是拿不到本地路径。
更新:自定义选择器代码
import SwiftUI import PhotosUI class ImagePickerViewModel: ObservableObject { // MARK: Properties @Published var fetchedImages: [ImageAsset] = [] @Published var selectedImages: [ImageAsset] = [] init(){ fetchImages() } // MARK: Fetching Images func fetchImages(){ let options = PHFetchOptions() // MARK: Modify As Per Your Wish options.includeHiddenAssets = false options.includeAssetSourceTypes = [.typeUserLibrary] options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] PHAsset.fetchAssets(with: .image, options: options).enumerateObjects { asset, _, _ in let imageAsset: ImageAsset = .init(asset: asset) self.fetchedImages.append(imageAsset) } PHAsset.fetchAssets(with: .video, options: options).enumerateObjects { asset, _, _ in let imageAsset: ImageAsset = .init(asset: asset) self.fetchedImages.append(imageAsset) } } }
视图代码
.popupImagePicker(show: $showPicker) { assets in // MARK: Do Your Operation With PHAsset // I'm Simply Extracting Image // .init() Means Exact Size of the Image let manager = PHCachingImageManager.default() let options = PHImageRequestOptions() options.isSynchronous = true DispatchQueue.global(qos: .userInteractive).async { assets.forEach { asset in manager.requestImage(for: asset, targetSize: .init(), contentMode: .default, options: options) { image, _ in guard let image = image else {return} DispatchQueue.main.async { self.pickedImages.append(image) } } } } }
解决方案
相册中的视频资源受系统权限保护,不能直接用URL(string:)构造路径,需要通过系统API获取可访问的本地URL,以下是两种可行方法:
方法1:导出视频到临时文件获取URL
通过PHAssetResourceManager将视频导出到App临时目录,得到可用于Firebase上传的URL:
.popupImagePicker(show: $showPicker) { assets in let resourceManager = PHAssetResourceManager.default() DispatchQueue.global(qos: .background).async { assets.forEach { asset in // 区分图片和视频处理逻辑 guard asset.mediaType == .video else { // 保留你原有的图片处理代码 let manager = PHCachingImageManager.default() let options = PHImageRequestOptions() options.isSynchronous = true manager.requestImage(for: asset, targetSize: .init(), contentMode: .default, options: options) { image, _ in guard let image = image else {return} DispatchQueue.main.async { self.pickedImages.append(image) } } return } // 获取视频资源对象 guard let videoResource = PHAssetResource.assetResources(for: asset).first(where: { $0.type == .video }) else { return } // 创建临时文件路径 let tempDir = FileManager.default.temporaryDirectory let tempVideoURL = tempDir.appendingPathComponent("\(UUID().uuidString).mov") // 导出视频到临时文件 let requestOptions = PHAssetResourceRequestOptions() requestOptions.isNetworkAccessAllowed = true // 允许从iCloud获取视频 resourceManager.writeData(for: videoResource, toFile: tempVideoURL, options: requestOptions) { error in if let error = error { print("视频导出失败:\(error.localizedDescription)") return } // 临时URL可直接用于Firebase上传 DispatchQueue.main.async { let storageRef = Storage.storage().reference().child("videos/\(UUID().uuidString).mov") let uploadTask = storageRef.putFile(from: tempVideoURL) // 监听上传状态 uploadTask.observe(.success) { _ in print("视频上传成功") // 上传完成后删除临时文件 try? FileManager.default.removeItem(at: tempVideoURL) } uploadTask.observe(.failure) { snapshot in if let error = snapshot.error as NSError? { print("上传失败:\(error.localizedDescription)") } try? FileManager.default.removeItem(at: tempVideoURL) } } } } } }
方法2:转换为Data直接上传
如果不想处理临时文件,可将视频转为Data,用Firebase的putData方法上传:
.popupImagePicker(show: $showPicker) { assets in DispatchQueue.global(qos: .background).async { assets.forEach { asset in guard asset.mediaType == .video else { // 原有图片处理逻辑 return } let videoOptions = PHVideoRequestOptions() videoOptions.isNetworkAccessAllowed = true PHImageManager.default().requestAVAsset(forVideo: asset, options: videoOptions) { avAsset, _, _ in guard let urlAsset = avAsset as? AVURLAsset else { return } let videoURL = urlAsset.url do { let videoData = try Data(contentsOf: videoURL) let storageRef = Storage.storage().reference().child("videos/\(UUID().uuidString).mov") let uploadTask = storageRef.putData(videoData) // 监听上传状态... } catch { print("读取视频数据失败:\(error.localizedDescription)") } } } } }
注意事项
- 确保App已申请相册访问权限,在
Info.plist中添加NSPhotoLibraryUsageDescription(读取权限)和NSPhotoLibraryAddUsageDescription(写入权限)。 - 若视频存储在iCloud,必须设置
isNetworkAccessAllowed = true,否则无法获取资源。 - 临时文件在上传完成后记得删除,避免占用沙盒空间。
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

