栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

如何在Swift中使用NSURLSession downloadTask顺序下载多个文件

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何在Swift中使用NSURLSession downloadTask顺序下载多个文件

您的代码无法正常

URLSessionDownloadTask
运行,因为它异步运行。因此,
BlockOperation
下载操作在下载完成之前完成,因此在依次触发操作的同时,下载任务将异步且并行地继续。

为了解决这个问题,您可以将请求包装在异步

Operation
子类中。有关更多信息,请参见《 并发编程指南》
中的“为并发执行配置操作


但是,在我说明如何根据您的情况(基于委托

URLSession
)来执行此操作之前,让我首先向您展示使用完成处理程序表示法时更简单的解决方案。稍后,我们将在此基础上解决您更复杂的问题。因此,在Swift
3及更高版本中:

class DownloadOperation : AsynchronousOperation {    var task: URLSessionTask!    init(session: URLSession, url: URL) {        super.init()        task = session.downloadTask(with: url) { temporaryURL, response, error in defer { self.finish() } guard     let httpResponse = response as? HTTPURLResponse,     200..<300 ~= httpResponse.statusCode else {     // handle invalid return pres however you'd like     return } guard let temporaryURL = temporaryURL, error == nil else {     print(error ?? "Unknown error")     return } do {     let manager = FileManager.default     let destinationURL = try manager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)         .appendingPathComponent(url.lastPathComponent)     try? manager.removeItem(at: destinationURL)        // remove the old one, if any     try manager.moveItem(at: temporaryURL, to: destinationURL)    // move new one there } catch let moveError {     print("(moveError)") }        }    }    override func cancel() {        task.cancel()        super.cancel()    }    override func main() {        task.resume()    }}

哪里

/// Asynchronous operation base class////// This is abstract to class emits all of the necessary KVO notifications of `isFinished`/// and `isExecuting` for a concurrent `Operation` subclass. You can subclass this and/// implement asynchronous operations. All you must do is:////// - override `main()` with the tasks that initiate the asynchronous task;////// - call `completeOperation()` function when the asynchronous task is done;////// - optionally, periodically check `self.cancelled` status, performing any clean-up///   necessary and then ensuring that `finish()` is called; or///   override `cancel` method, calling `super.cancel()` and then cleaning-up///   and ensuring `finish()` is called.class AsynchronousOperation: Operation {    /// State for this operation.    @objc private enum OperationState: Int {        case ready        case executing        case finished    }    /// Concurrent queue for synchronizing access to `state`.    private let stateQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".rw.state", attributes: .concurrent)    /// Private backing stored property for `state`.    private var rawState: OperationState = .ready    /// The state of the operation    @objc private dynamic var state: OperationState {        get { return stateQueue.sync { rawState } }        set { stateQueue.sync(flags: .barrier) { rawState = newValue } }    }    // MARK: - Various `Operation` properties    open         override var isReady:        Bool { return state == .ready && super.isReady }    public final override var isExecuting:    Bool { return state == .executing }    public final override var isFinished:     Bool { return state == .finished }    // KVO for dependent properties    open override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {        if ["isReady", "isFinished", "isExecuting"].contains(key) { return [#keyPath(state)]        }        return super.keyPathsForValuesAffectingValue(forKey: key)    }    // Start    public final override func start() {        if isCancelled { finish() return        }        state = .executing        main()    }    /// Subclasses must implement this to perform their work and they must not call `super`. The default implementation of this function throws an exception.    open override func main() {        fatalError("Subclasses must implement `main`.")    }    /// Call this function to finish an operation that is currently executing    public final func finish() {        if !isFinished { state = .finished }    }}

然后,您可以执行以下操作:

for url in urls {    queue.addOperation(DownloadOperation(session: session, url: url))}

因此,这是将异步

URLSession
/
NSURLSession
请求包装在异步
Operation
/
NSOperation
子类中的一种非常简单的方法。更一般而言,这是一种有用的模式,
AsynchronousOperation
用于将一些异步任务包装在
Operation
/
NSOperation
对象中。

不幸的是,在您的问题中,您想使用基于委托的

URLSession
/,
NSURLSession
以便可以监视下载进度。这更复杂。

这是因为

NSURLSession
在会话对象的委托处调用了“任务完成”
委托方法。这是的令人毛骨悚然的设计功能
NSURLSession
(但是Apple这样做是为了简化后台会议,在这里不相关,但是我们受制于该设计限制)。

但是,我们必须在任务完成时异步完成操作。因此,我们需要某种方式让会话确定何时

didCompleteWithError
调用哪个操作才能完成。现在您可以使每个操作都有其自己的
NSURLSession
对象,但是事实证明这效率很低。

因此,为了解决这个问题,我维护了一个字典,该字典以任务的键为关键字

taskIdentifier
,用于标识适当的操作。这样,下载完成后,您可以“完成”正确的异步操作。从而:

/// Manager of asynchronous download `Operation` objectsclass DownloadManager: NSObject {    /// Dictionary of operations, keyed by the `taskIdentifier` of the `URLSessionTask`    fileprivate var operations = [Int: DownloadOperation]()    /// Serial OperationQueue for downloads    private let queue: OperationQueue = {        let _queue = OperationQueue()        _queue.name = "download"        _queue.maxConcurrentOperationCount = 1    // I'd usually use values like 3 or 4 for performance reasons, but OP asked about downloading one at a time        return _queue    }()    /// Delegate-based `URLSession` for DownloadManager    lazy var session: URLSession = {        let configuration = URLSessionConfiguration.default        return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)    }()    /// Add download    ///    /// - parameter URL:  The URL of the file to be downloaded    ///    /// - returns:        The DownloadOperation of the operation that was queued    @discardableResult    func queueDownload(_ url: URL) -> DownloadOperation {        let operation = DownloadOperation(session: session, url: url)        operations[operation.task.taskIdentifier] = operation        queue.addOperation(operation)        return operation    }    /// Cancel all queued operations    func cancelAll() {        queue.cancelAllOperations()    }}// MARK: URLSessionDownloadDelegate methodsextension DownloadManager: URLSessionDownloadDelegate {    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {        operations[downloadTask.taskIdentifier]?.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)    }    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {        operations[downloadTask.taskIdentifier]?.urlSession(session, downloadTask: downloadTask, didWritedata: bytesWritten, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite)    }}// MARK: URLSessionTaskDelegate methodsextension DownloadManager: URLSessionTaskDelegate {    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)  {        let key = task.taskIdentifier        operations[key]?.urlSession(session, task: task, didCompleteWithError: error)        operations.removevalue(forKey: key)    }}/// Asynchronous Operation subclass for downloadingclass DownloadOperation : AsynchronousOperation {    let task: URLSessionTask    init(session: URLSession, url: URL) {        task = session.downloadTask(with: url)        super.init()    }    override func cancel() {        task.cancel()        super.cancel()    }    override func main() {        task.resume()    }}// MARK: NSURLSessionDownloadDelegate methodsextension DownloadOperation: URLSessionDownloadDelegate {    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {        guard let httpResponse = downloadTask.response as? HTTPURLResponse, 200..<300 ~= httpResponse.statusCode        else { // handle invalid return pres however you'd like return        }        do { let manager = FileManager.default let destinationURL = try manager     .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)     .appendingPathComponent(downloadTask.originalRequest!.url!.lastPathComponent) try? manager.removeItem(at: destinationURL) try manager.moveItem(at: location, to: destinationURL)        } catch { print(error)        }    }    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {        let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)        print("(downloadTask.originalRequest!.url!.absoluteString) (progress)")    }}// MARK: URLSessionTaskDelegate methodsextension DownloadOperation: URLSessionTaskDelegate {    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)  {        defer { finish() }        if let error = error { print(error) return        }        // do whatever you want upon success    }}

然后像这样使用它:

let downloadManager = DownloadManager()override func viewDidLoad() {    super.viewDidLoad()    let urlStrings = [        "http://spaceflight.nasa.gov/gallery/images/apollo/apollo17/hires/s72-55482.jpg",        "http://spaceflight.nasa.gov/gallery/images/apollo/apollo10/hires/as10-34-5162.jpg",        "http://spaceflight.nasa.gov/gallery/images/apollo-soyuz/apollo-soyuz/hires/s75-33375.jpg",        "http://spaceflight.nasa.gov/gallery/images/apollo/apollo17/hires/as17-134-20380.jpg",        "http://spaceflight.nasa.gov/gallery/images/apollo/apollo17/hires/as17-140-21497.jpg",        "http://spaceflight.nasa.gov/gallery/images/apollo/apollo17/hires/as17-148-22727.jpg"    ]    let urls = urlStrings.compactMap { URL(string: $0) }    let completion = BlockOperation {        print("all done")    }    for url in urls {        let operation = downloadManager.queueDownload(url)        completion.addDependency(operation)    }    OperationQueue.main.addOperation(completion)}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/413202.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号