2

我正在尝试在 Swift 中使用 NSURLSession 同时下载多个文件。我想将所有下载进度状态合并为一个,以便在下载所有文件时显示 100%。目前我为每个文件下载完成获得 100%,但只有当所有文件都下载完成时我才需要 100%。我怎样才能在 Swift 中实现这一点?

这是我的 DownloadManager 类:

class DownloadManager : NSObject, URLSessionDelegate, URLSessionDownloadDelegate {

static var shared = DownloadManager()
var task1Progress = 0.00
var task2Progress = 0.00
typealias ProgressHandler = (Float) -> ()

var onProgress : ProgressHandler? {
    didSet {
        if onProgress != nil {
            let _ = activate()
        }
    }
}

override private init() {
    super.init()
}

func activate() -> URLSession {
    let config = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).background")

    // Warning: If an URLSession still exists from a previous download, it doesn't create a new URLSession object but returns the existing one with the old delegate object attached!
    return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
}

func calculateProgress(session : URLSession, completionHandler : @escaping (Float) -> ()) {
    session.getTasksWithCompletionHandler { (tasks, uploads, downloads) in
        let progress = downloads.map({ (task) -> Float in
            if task.countOfBytesExpectedToReceive > 0 {
                return Float(task.countOfBytesReceived) / Float(task.countOfBytesExpectedToReceive)
            } else {
                return 0.0
            }
        })
        completionHandler(progress.reduce(0.0, +))
    }
}


func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {

    if totalBytesExpectedToWrite > 0 {
        if let onProgress = onProgress {
            calculateProgress(session: session, completionHandler: onProgress)
        }

        let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
        debugPrint("Download Progress \(downloadTask) \(progress)")

    }
}

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
    debugPrint("Download finished: \(location)")
    try? FileManager.default.removeItem(at: location)
}


func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    debugPrint("Task completed: \(task), error: \(String(describing: error))")
}

private func calculateProgress(session : URLSession, completionHandler : @escaping (Float) -> ()) {
    session.getTasksWithCompletionHandler { (tasks, uploads, downloads) in
        let progress = downloads.map({ (task) -> Float in
            if task.countOfBytesExpectedToReceive > 0 {

                if (task.taskIdentifier ==  1) {

                    self.task1Progress = Double(Float(task.countOfBytesReceived) / Float(task.countOfBytesExpectedToReceive))

                } else if (task.taskIdentifier ==  2){

                    self.task2Progress = Double(Float(task.countOfBytesReceived) / Float(task.countOfBytesExpectedToReceive))

                }

                print("pro1 = \(self.task1Progress) pro2 = \(self.task2Progress)")

                if(self.task1Progress>0.0000 && self.task2Progress>0.000) {
                    return Float(min(self.task1Progress  ,self.task2Progress))
                }

                return Float(max(self.task1Progress  ,self.task2Progress))

            } else {

                return 0.0
            }
        })
        completionHandler(progress.reduce(0.0, +))
    }
}

}

4

4 回答 4

0

我有类似的要求,这就是我设法解决的方法,对我来说,我有一个主题列表,每个主题topic都有章节,每个chapter was a file. 所以当一个主题被下载时,我必须下载它的章节。所以这里有一个问题,可以说一个主题有15文件(我从元数据中获取数字)15 download objects and 15 download taskes,每当每个任务触发进度时,我都会在下面的函数中处理它。这个想法很简单,每个文件0.0 to 1.0都从头到尾,所以当所有 15 个文件完成时,所有进度的总和为 15.0,这意味着所有下载在the sum of progress == total number of files

    func handleProgress(percentage:Float){
// Array of files
        let totalFileCount = downloadData.count
        totalPercentageProgress += percentage
        DispatchQueue.main.async {
            self.downloadProgressView.progress = self.totalPercentageProgress / Float(totalFileCount)
        }
    }

-----

    public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64){

        if totalBytesExpectedToWrite > 0 {
            let progressPercentage = Float(totalBytesWritten)/Float(totalBytesExpectedToWrite)
            let progressHandler = getProgressHandlerForTask(identifier: downloadTask.taskIdentifier)
            print(progressPercentage)
            delegate?.handleProgress(progressPercentage)
        }

    }
于 2020-03-06T15:34:35.723 回答
0

用于存储数据和计算进度的变量

var progress: Float = 0
var expectedContentLength: Int64 = 0
var allData: Data = Data()

创建默认会话:

let defaultConfiguration = URLSessionConfiguration.default
let defaultSession = URLSession(configuration: defaultConfiguration, delegate: self, delegateQueue: nil

从 url 下载数据,您可以根据需要多次调用此方法

func downlod(from url: URL, session: URLSession) {
    let dataTask = session.dataTask(with: url)
    dataTask.resume();
}

并且您需要实施以下代表

URLSessionDelegate, URLSessionDataDelegate 

public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Swift.Void) {

    progress = 0
    expectedContentLength += response.expectedContentLength
    completionHandler(.allow)
}


public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
    allData.append(data)
    progress = Float(allData.count) / Float(expectedContentLength)
    print("progress - \(progress)")
}

开始下载时不要忘记重置 expectedContentLength 变量,如下所示

expectedContentLength = 0
于 2018-03-27T11:59:37.763 回答
0

您可以使用 dispatchgroup tp 实现所需的行为。

要下载文件,您将调用downloadManager. 在 dispatchGroup 上调度下载操作,并在通知中您将在所有文件下载完成后收到回调。

请在下面找到示例:

func dispatchGroupUsage (completion: CallBack) {
let backgroundQ = DispatchQueue.global(attributes: .qosDefault)
let group = DispatchGroup()

for number in numberOfFilesToBeDownloaded {
  group.enter()
  backgroundQ.async(group: group,  execute: {  
      // call download manager's method to download files
      group.leave()

      })
  }

 group.notify(queue: DispatchQueue.main, execute: {
   print("All Done"); completion(result: fill)
 }) 
}
于 2018-03-27T11:55:36.760 回答
-1

只需使用NSProgress。它可以让你做你想做的事。

每个 NSURLSessionTask 都有一个进度属性(如果你的目标是旧操作系统,你可以自己创建一个)。

然后只需创建一个父 NSProgress 实例并将每个任务进度添加为子项。

最后,观察fractionCompletedNSProgress 的属性并更新你的进度指示器。

于 2018-03-27T12:07:11.857 回答