Swift中如何将.m3u8文件保存至本地磁盘以支持分享下载?
解决m3u8离线缓存文件的导出与分享问题
你当前使用AVAssetDownloadTask实现的离线缓存,是苹果专为HLS播放设计的私有缓存格式,存储在沙盒的隐蔽目录中,无法直接分享。要实现可分享的本地文件,需要将缓存的资产导出为标准视频格式(如MP4),或直接下载原始分片并合并。以下是两种可行方案:
方案一:导出已缓存的离线资产为可分享文件
利用AVAssetExportSession将已缓存的AVURLAsset导出到可访问的目录(如Documents),这样就能通过系统分享功能发送给其他应用。
1. 添加导出方法到DownloadManager
修改你的DownloadManager类,新增导出逻辑:
class DownloadManager: NSObject { static let shared = DownloadManager() // 原setupAssetDownload方法保持不变 func setupAssetDownload(url:URL) { let configuration = URLSessionConfiguration.background(withIdentifier: "streamVideo") let downloadSession = AVAssetDownloadURLSession(configuration: configuration, assetDownloadDelegate: self, delegateQueue: OperationQueue.main) let asset = AVURLAsset(url: url) let downloadTask = downloadSession.makeAssetDownloadTask(asset: asset, assetTitle: "", assetArtworkData: nil, options: nil) downloadTask?.resume() } // 新增导出方法 func exportCachedAssetToShareableFile(completion: @escaping (URL?, Error?) -> Void) { guard let assetPath = UserDefaults.standard.value(forKey: "assetPath") as? String else { completion(nil, NSError(domain: "DownloadError", code: 1, userInfo: [NSLocalizedDescriptionKey: "无可用离线资产"])) return } let baseURL = URL(fileURLWithPath: NSHomeDirectory()) let assetURL = baseURL.appendingPathComponent(assetPath) let asset = AVURLAsset(url: assetURL) guard let cache = asset.assetCache, cache.isPlayableOffline else { completion(nil, NSError(domain: "DownloadError", code: 2, userInfo: [NSLocalizedDescriptionKey: "资产无法离线播放"])) return } // 设置导出路径(Documents目录,可自定义文件名) let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let outputURL = documentsDir.appendingPathComponent("exported_video.mp4") // 清理已存在的同名文件 if FileManager.default.fileExists(atPath: outputURL.path) { try? FileManager.default.removeItem(at: outputURL) } // 创建导出会话 guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) else { completion(nil, NSError(domain: "ExportError", code: 3, userInfo: [NSLocalizedDescriptionKey: "无法创建导出会话"])) return } exportSession.outputURL = outputURL exportSession.outputFileType = .mp4 exportSession.shouldOptimizeForNetworkUse = false // 执行导出 exportSession.exportAsynchronously { DispatchQueue.main.async { switch exportSession.status { case .completed: completion(outputURL, nil) case .failed, .cancelled: completion(nil, exportSession.error ?? NSError(domain: "ExportError", code: 4, userInfo: [NSLocalizedDescriptionKey: "导出失败"])) default: break } } } } } // 原AVAssetDownloadDelegate扩展保持不变 extension DownloadManager: AVAssetDownloadDelegate { func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) { UserDefaults.standard.set(location.relativePath, forKey: "assetPath") } }
2. 调用导出并分享文件
在需要分享的地方调用导出方法,完成后用系统分享控制器发送:
DownloadManager.shared.exportCachedAssetToShareableFile { url, error in if let exportURL = url { let activityVC = UIActivityViewController(activityItems: [exportURL], applicationActivities: nil) // iPad需要设置popoverPresentationController if let popover = activityVC.popoverPresentationController { popover.sourceView = self.view popover.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0) } self.present(activityVC, animated: true) } else { print("导出失败:\(error?.localizedDescription ?? "未知错误")") // 这里可以弹出错误提示给用户 } }
方案二:直接下载原始m3u8分片并合并(适合需要原始文件场景)
如果需要获取原始的m3u8和TS分片,需要自行解析m3u8文件、下载所有分片并合并。但这种方法需要处理HLS加密(如果有的话),实现复杂度较高,且苹果原生的AVAssetDownloadTask已自动处理加密,因此更推荐方案一。
若要实现此方案,需:
- 解析m3u8文件内容,提取所有TS分片的URL
- 批量下载TS分片到本地
- 使用
AVAssetWriter或第三方工具将分片合并为标准视频文件
注意事项
- 导出后的文件会占用额外磁盘空间,可选择在导出完成后删除原缓存文件
- 导出时间取决于视频长度和设备性能,建议在后台执行并显示进度
- 需处理磁盘空间不足、导出失败等异常情况
内容的提问来源于stack exchange,提问作者Dev
相关产品推荐
相关产品推荐

