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

SwiftUI中PhotosPicker选择视频后文件体积大于原文件的问题排查

SwiftUI中PhotosPicker选择视频后文件体积大于原文件的问题排查

你好呀,我来帮你捋捋这个问题~首先你的现象很明确:用PhotosPicker选视频后拿到的文件是8.9MB,比原文件的5.3MB大,但用UIImagePickerController就能拿到和原文件一样大小的,这其实是PhotosPicker的默认转码行为导致的,咱们来一步步分析原因和解决办法:

问题根源:自动转码导致体积变大

iOS的PhotosPicker在通过Transferable协议处理视频时,默认会把视频转码成系统通用兼容的格式(比如把高效的HEVC/H.265编码转成兼容性更广的H.264编码)。而H.264的压缩率比HEVC低,所以转码后的文件体积自然会变大。反观UIImagePickerController,它是直接获取相册里的原文件URL,没有经过转码步骤,所以大小和原文件一致。

另外还要提一句你代码里的小问题:在Transferable的importing闭包里,你只是创建了目标URL,但没有执行实际的文件复制操作,这样后续访问copy这个URL会找不到文件哦,正确的复制逻辑应该加上文件拷贝的代码(不过即使加上,复制的也是转码后的文件,体积还是会大)。

解决方案:获取原文件,绕过转码

要拿到和原文件大小一致的视频,咱们可以直接获取相册里的原资产(PHAsset),然后从中提取原文件的URL,完全绕过Transferable的转码流程。修改你ContentView里的onChange代码就行:

.onChange(of: selectedItem) { _ in
    Task {
        do {
            // 加载原始PHAsset
            if let asset = try await selectedItem?.loadOriginalAsset() {
                let options = PHVideoRequestOptions()
                options.isNetworkAccessAllowed = true // 如果视频存在iCloud上,需要开启这个
                options.version = .original // 指定获取原版本视频
                
                // 请求原视频的AVAsset
                PHImageManager.default().requestAVAsset(forVideo: asset, options: options) { avAsset, audioMix, info in
                    if let urlAsset = avAsset as? AVURLAsset {
                        let originalURL = urlAsset.url
                        print("原文件大小:\(originalURL.fileSize)")
                        
                        // 把原文件复制到你的应用目录(可选,根据需求来)
                        let destinationURL = URL.documentsDirectory.appending(path: "abc.mp4")
                        do {
                            // 先删除已存在的文件(避免覆盖报错)
                            if FileManager.default.fileExists(atPath: destinationURL.path) {
                                try FileManager.default.removeItem(at: destinationURL)
                            }
                            try FileManager.default.copyItem(at: originalURL, to: destinationURL)
                            print("复制完成,目标文件大小:\(destinationURL.fileSize)")
                        } catch {
                            print("复制文件出错:\(error.localizedDescription)")
                        }
                    }
                }
            }
        } catch {
            print("加载资产出错:\(error.localizedDescription)")
        }
    }
}

补充:如果坚持用Transferable怎么办?

如果你不想用PHAsset的方式,也可以尝试给FileRepresentation指定更精确的视频类型,比如直接指定HEVC的contentType,让系统不要转码:

static var transferRepresentation: some TransferRepresentation {
    FileRepresentation(contentType: .videoHEVC) { movie in
        SentTransferredFile(movie.url)
    } importing: { received in
        print("Importing \(received)")
        print("receive \(received.file.fileSize)")
        
        let copyURL = URL.documentsDirectory.appending(path: "abc.mp4")
        // 加上实际的复制操作
        try FileManager.default.copyItem(at: received.file, to: copyURL)
        
        return Self.init(url: copyURL)
    }
}

不过这个方法有局限性:如果用户选择的视频不是HEVC编码的,就会失败,所以还是推荐第一种获取原PHAsset的方式更稳妥。

备注:内容来源于stack exchange,提问作者William Hu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 10:19:27