You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xcode14下iOS设备视频信息(URL/时长等)获取失败求助

解决iOS获取设备视频信息(URL/时长/缩略图/标题)的问题

现有方案的问题分析

  1. PHAsset方法:PHAsset.fetchAssets仅能获取系统照片应用库内的视频,Safari下载到Downloads文件夹的视频不在Photos框架的索引范围内,因此无法获取。此外,代码中每次循环创建PHCachingImageManager会导致资源浪费,应复用实例。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 01:20:15