Xcode14下iOS设备视频信息(URL/时长等)获取失败求助
解决iOS获取设备视频信息(URL/时长/缩略图/标题)的问题
现有方案的问题分析
- PHAsset方法:
PHAsset.fetchAssets仅能获取系统照片应用库内的视频,Safari下载到Downloads文件夹的视频不在Photos框架的索引范围内,因此无法获取。此外,代码中每次循环创建PHCachingImageManager会导致资源浪费,应复用实例。 - FileManager方法:代码中访问的是
Documents目录,但Safari下载的视频存储在Downloads目录,路径错误导致找不到文件。
正确解决方案
需要分两种场景处理:获取Photos库内的视频,以及获取Downloads文件夹内的视频。同时需确保权限配置完整。
1. 权限补充配置
除了照片访问权限,若要访问Downloads文件夹,需在Info.plist中添加:
<key>NSDownloadsFolderUsageDescription</key> <string>需要访问Downloads文件夹获取您下载的视频</string>
2. 获取Downloads文件夹内的视频
以下代码可遍历Downloads目录,提取视频的URL、时长、缩略图和标题:
// 定义模型存储视频信息 struct VideoInfo { let url: URL let duration: Double let thumbnail: UIImage let title: String } func fetchDownloadsVideos(completion: @escaping ([VideoInfo]) -> Void) { guard let downloadsDir = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first else { completion([]) return } var videoInfos: [VideoInfo] = [] let dispatchGroup = DispatchGroup() do { // 获取目录下所有文件,过滤隐藏文件 let fileURLs = try FileManager.default.contentsOfDirectory(at: downloadsDir, includingPropertiesForKeys: [.isRegularFileKey], options: .skipsHiddenFiles) for url in fileURLs { let ext = url.pathExtension.lowercased() // 过滤视频格式,仅处理普通文件 guard ["mov", "mp4", "mkv"].contains(ext), (try? url.resourceValues(forKeys: [.isRegularFileKey]))?.isRegularFile == true else { continue } dispatchGroup.enter() let avAsset = AVAsset(url: url) // 计算视频时长(秒) let duration = avAsset.duration.seconds // 生成缩略图 let imageGenerator = AVAssetImageGenerator(asset: avAsset) imageGenerator.appliesPreferredTrackTransform = true // 保持视频方向 do { let cgImage = try imageGenerator.copyCGImage(at: CMTime.zero, actualTime: nil) let thumbnail = UIImage(cgImage: cgImage) // 标题默认使用文件名(去除后缀),也可读取元数据获取更精准标题 let title = url.lastPathComponent.replacingOccurrences(of: ".\(ext)", with: "") videoInfos.append(VideoInfo(url: url, duration: duration, thumbnail: thumbnail, title: title)) } catch { print("生成缩略图失败:\(error)") } dispatchGroup.leave() } } catch { print("访问Downloads目录失败:\(error)") completion([]) return } dispatchGroup.notify(queue: .main) { completion(videoInfos) } }
3. 获取Photos库内的视频
修正原PHAsset方法,复用PHCachingImageManager,并统一提取所需信息:
func fetchPhotosLibraryVideos(completion: @escaping ([VideoInfo]) -> Void) { let imageManager = PHCachingImageManager() let fetchOptions = PHFetchOptions() fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.video.rawValue) let allVideos = PHAsset.fetchAssets(with: .video, options: fetchOptions) var videoInfos: [VideoInfo] = [] let dispatchGroup = DispatchGroup() allVideos.enumerateObjects { asset, _, _ in dispatchGroup.enter() // 获取标题(优先用系统标注的标题,无则用默认值) let title = asset.localizedTitle ?? "未命名视频" // 获取时长(PHAsset.duration单位为毫秒,转成秒) let duration = Double(asset.duration) / 1000.0 // 获取缩略图 let targetSize = CGSize(width: 200, height: 200) imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: nil) { thumbnail, _ in guard let thumbnailImage = thumbnail else { dispatchGroup.leave() return } // 获取视频URL imageManager.requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in guard let urlAsset = avAsset as? AVURLAsset else { dispatchGroup.leave() return } videoInfos.append(VideoInfo(url: urlAsset.url, duration: duration, thumbnail: thumbnailImage, title: title)) dispatchGroup.leave() } } } dispatchGroup.notify(queue: .main) { completion(videoInfos) } }
使用示例
// 获取Downloads视频 fetchDownloadsVideos { videos in print("Downloads视频数量:\(videos.count)") // 处理视频数据 } // 获取Photos库视频 fetchPhotosLibraryVideos { videos in print("Photos库视频数量:\(videos.count)") // 处理视频数据 }
内容的提问来源于stack exchange,提问作者costomato
相关产品推荐
相关产品推荐

