iOS 正在恢复 HLS 下载并删除部分下载的文件

iOS Resuming HLS download and deleting partially downloaded file

我正在按照 Apple Docs

实施 HLS 流

但我面临的问题是当用户终止应用程序时恢复下载。如果下载正在进行并说它已完成 50% 并且用户终止了该应用程序或应用程序由于任何原因被系统终止并且当该应用程序再次活动时那么 URL 会话委托 didCompleteWithError被称为

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
}

这里我没有部分下载的文件路径或恢复任务的能力。

通过以下委托调用完成下载后,将调用下载文件的唯一位置

func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) {
}

现在医生说要使用

downloadSession.getAllTasks { tasksArray in }

但不幸的是,它没有恢复下载

所以我的问题是

  1. 如何从该下载状态恢复任务,以便整个下载不会从 0% 重新开始?
  2. 对于不可恢复的任务或我不想恢复的特定场景,如何删除部分下载的文件?如何获取下载路径(我不想搜索整个文档目录)

实际上,您可以使用 getAllTasks(completionHandler:)] 来获取在上次启动时未完成的待处理任务 但是不知何故这些任务将在创建下载会话后立即取消如您所见,这导致 urlSession(_:task:didCompleteWithError:) 被调用。

幸运的是,我找到了另一种恢复方式AVAssetDownloadTask

AVAssetDownloadTask provides the ability to resume previously stopped downloads under certain circumstances. To do so, simply instantiate a new AVAssetDownloadTask with an AVURLAsset instantiated with a file NSURL pointing to the partially downloaded bundle with the desired download options, and the download will continue restoring any previously downloaded data.

这意味着如果你想恢复一个挂起的AVAssetDownloadTask,你必须在下载任务停止时从urlSession(_:assetDownloadTask:didFinishDownloadingTo:)保存location。之后,根据部分下载的文件创建另一个下载任务。

func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) {
    destinationURL = location
}

func resumeDownloadTask() {
    let urlAsset = AVURLAsset(url: destinationURL)
    downloadTask = assetDownloadURLSession.makeAssetDownloadTask(asset: urlAsset, assetTitle: "title", assetArtworkData: nil, options: nil)
    downloadTask.resume()
}

urlSession(_:assetDownloadTask:didFinishDownloadingTo:) 将始终在 urlSession(_:task:didCompleteWithError:) 之前调用,因此在应用程序终止和重新启动或下载任务被取消的两种情况下,您都可以获得 destinationURL

请注意,您不应在 urlSession(_:task:didCompleteWithError:) 中创建新的下载任务,否则会导致无限循环。

对于第二个问题,只需使用 destinationURL.

删除文件即可

有关更多详细信息,我在下面创建了一个示例存储库 link。仍然存在一些错误,但在正常情况下可以 运行 。尝试开始下载任务,让它 运行s 一段时间并终止应用程序。重新启动并恢复任务,您将看到结果。

https://github.com/trungducc/Whosebug/tree/hls-download-resuming

虽然接受的答案很有效,但这里有一个重要的补充。

从 Xcode 调试时,应用程序安装目录会在每次重新启动时发生变化,导致资产目标位置在重新启动后变得无效。

您实际上可以坚持 location.relativeString 并在下次启动时重新构建更新的 URL,如下所示:

// get it from UserDefaults most likely
let persistedRelativeString = .... 

// this corresponds to the app installation root path
let baseDownloadDirectory = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!.deletingLastPathComponent()

let updatedLocation = URL(string: persistedRelativeString, relativeTo: baseDownloadDirectory)!

On iOS 14+ 似乎会话自动恢复下载任务,除非您手动取消它。了解此行为后,我们可以简单地让它继续,一旦 urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) 被调用,您就可以检索此任务的有效路径。

要支持多个任务,您可以坚持一个 [Int: String] 对应于:

  • 键:任务 ID(重新启动时稳定)
  • 值:location.relativeString.

通过这种方式,您可以轻松实现跨重启下载。