【代码片-1】 IOS开发Swift用NSURLSessionDownloadTask实现后台下载

浏览: 59 发布日期: 2016-11-30 分类: swift
//
//  JPCDownloadHelper.swift
//  BTSDemo
//
//  Created by jianxiong li on 16/3/10.
//  Copyright © 2016年 joobot. All rights reserved.
//

import Foundation


var helpers:NSMutableDictionary = NSMutableDictionary()

protocol JPCDownloadStatusDelegate{
    func bytesReceived(receivedBytes:Int64,totalbytes:Int64,url:String)
    func downloadFinishedForFile(url:String,localFileUrl:String)
    func downloadResumdForFile(url:String)
    func downloadErrorForFile(url:String)
    func sessionActive()
}

class JPCDownloadHelper:NSObject,NSURLSessionDelegate,NSURLSessionDownloadDelegate{
    
    var session:NSURLSession?
    var resumeDatas:NSMutableDictionary
    var delegate:JPCDownloadStatusDelegate?
    var backgroundCompletionHandler:(()->())?
    
    class func downloadHelperForIdentifier(identifier:String)->JPCDownloadHelper{
        let helper:JPCDownloadHelper? = helpers.objectForKey(identifier) as? JPCDownloadHelper
        print("downloadHelperForIdentifier.......helper:\(helper)")
        if(helper == nil){
            return JPCDownloadHelper(identifier: identifier)
        }else{
            return helper!
        }
    }
    
    //便利构造器
//    convenience init(){
//        self.init(identifier: "com.joobot.ljx.btsdemo")
//    }
    
    //指定构造器
    private init(identifier:String){
        self.resumeDatas = NSMutableDictionary()
        super.init()
        print("JPCDownloadHelper init.......session:\(session) identifier:\(identifier)")
        if(self.session == nil){
            let config = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(identifier)
            self.session = NSURLSession(configuration: config, delegate: self, delegateQueue: nil)
            helpers.setObject(self, forKey: identifier)
        }
    }
    
    //激活页面:退出页面,再退出程序后,重新进入页面
    // When we connect a UI delegate, the session could be running tasks if app previously crashed. Notify UI delegate asynchronously if so.
    class func setDelega(dele:JPCDownloadStatusDelegate,identifier:String){
        let helper:JPCDownloadHelper? = helpers.objectForKey(identifier) as? JPCDownloadHelper
        print("===============================setDelegate.......helper:\(helper)")
        if(helper != nil){
            helper?.delegate = dele
            //            helper?.session?.getTasksWithCompletionHandler({
            //                dataTasks,uploadTasks,downloadTasks in
            //                print("===============================setDelegate.......downloadTasks:\(downloadTasks)")
            //                if(downloadTasks.count>0){
            //                    dispatch_async(dispatch_get_main_queue(), {
            //                        helper?.delegate?.sessionActive()
            //                    })
            //                }
            //            })
        }
    }
    
    
    func downloadFile(urlString:String){
        print("JPCDownloadHelper downloadFile.......urlString:\(urlString)")
        let request:NSURLRequest = NSURLRequest(URL: NSURL(string: urlString)!)
        
        var downloadTask:NSURLSessionDownloadTask?
        let resumeData:NSData? = resumeDatas.objectForKey(urlString) as? NSData
        
        print("JPCDownloadHelper downloadFile.......resumeData:\(resumeData)")
        if(resumeData != nil){
            downloadTask = self.session?.downloadTaskWithResumeData(resumeData!)
        }
        
        print("JPCDownloadHelper downloadFile.......downloadTask:\(downloadTask) session:\(session)")
        if(downloadTask == nil){
            resumeDatas.removeObjectForKey(urlString)
            downloadTask = session?.downloadTaskWithRequest(request)
        }else{
            dispatch_async(dispatch_get_main_queue(),{
                self.delegate?.downloadResumdForFile(urlString)
            })
        }
        print("JPCDownloadHelper downloadFile.......downloadTask:\(downloadTask)")
        downloadTask?.resume()
    }
    

    
     //pragma mark - NSURLSessionDelegate implementation
    @objc func URLSessionDidFinishEventsForBackgroundURLSession(session: NSURLSession) {
         print("JPCDownloadHelper didReceiveChallenge---------- ")
        let completionhandler = self.backgroundCompletionHandler
        self.backgroundCompletionHandler = nil
        
        dispatch_async(dispatch_get_main_queue(), {
            completionhandler!()
        })
        
        session.invalidateAndCancel()
        helpers.removeObjectForKey(session.configuration.identifier!)
    }
    
    //挑战处理类型为 默认
    /*
    NSURLSessionAuthChallengePerformDefaultHandling:默认方式处理
    NSURLSessionAuthChallengeUseCredential:使用指定的证书
    NSURLSessionAuthChallengeCancelAuthenticationChallenge:取消挑战
    */
    @objc func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
        print("JPCDownloadHelper didReceiveChallenge---------- challenge:\(challenge) completionHandler:\(completionHandler)")
        let credential:NSURLCredential? = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)
        
        var disposition = NSURLSessionAuthChallengeDisposition.UseCredential
        
        // 确定挑战的方式
        if (credential != nil) {
            disposition = NSURLSessionAuthChallengeDisposition.UseCredential;
        } else {
            disposition = NSURLSessionAuthChallengeDisposition.PerformDefaultHandling;
        }
        
        // 必须调用此方法,完成认证挑战
        completionHandler(disposition,credential)
    }
    
    @objc func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
        print("JPCDownloadHelper didBecomeInvalidWithError............")
    }
    
  //pragma mark - NSURLSessionDownloadDelegate implementation
    
    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
        
        let localPath = location.absoluteString
        let originPath = downloadTask.originalRequest!.URL!.absoluteString
        print("JPCDownloadHelper didFinishDownloadingToURL............localPath:\(localPath) originPaht:\(originPath)")
        
        self.saveFileToDisk(location,originUrl:downloadTask.originalRequest!.URL!)
        dispatch_async(dispatch_get_main_queue(), {
           self.delegate?.downloadFinishedForFile(originPath, localFileUrl: localPath)
        });
    }
    
    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
         print("JPCDownloadHelper didResumeAtOffset............")
    }
    
    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
         print("JPCDownloadHelper didWriteData............bytesWritten:\(bytesWritten)  totalBytesWritten:\(totalBytesWritten) totalBytesExpectedToWrite:\(totalBytesExpectedToWrite)")
        dispatch_async(dispatch_get_main_queue(), {
            self.delegate?.bytesReceived(totalBytesWritten, totalbytes: totalBytesExpectedToWrite, url: downloadTask.originalRequest!.URL!.absoluteString)
        });
    }
    

    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
         print("JPCDownloadHelper didCompleteWithError............task.originalRequest!.URL!.absoluteString:\(task.originalRequest!.URL!.absoluteString)")
        if(error == nil){
            resumeDatas.removeObjectForKey(task.originalRequest!.URL!.absoluteString)
        }else{
            let resumeData = error?.userInfo[NSURLSessionDownloadTaskResumeData]
            if(resumeData != nil){
                dispatch_async(dispatch_get_main_queue(),{
                    self.resumeDatas.setObject(resumeData!, forKey: task.originalRequest!.URL!.absoluteString)
                    self.delegate?.downloadErrorForFile(task.originalRequest!.URL!.absoluteString)
                })
            }
        }
        
    }

    
}

extension JPCDownloadHelper{
    func saveFileToDisk(location: NSURL,originUrl:NSURL){
        
        //获取后缀名
        //        var urlPath = originPath as NSString
        //        var local: Int = 0
        //        while(urlPath.rangeOfString("/")).location != NSNotFound{
        //
        //            local = (urlPath.rangeOfString("/")).location;
        //            urlPath = urlPath.substringFromIndex(local+1)
        //        }
        //        if (urlPath.rangeOfString(".")).location == NSNotFound {
        //            print("saveFileToDisk name = \(urlPath) if not file name return")
        //            return
        //        }
        //        let name = "/" + (urlPath as String)
        
        
        
        //        let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
        //        let documentsPath = paths[0] as String
        //        let destFilepath = documentsPath.stringByAppendingString(desName)
        //        print("destFilepath = " + destFilepath)
        
        //        //删除已存在的文件
        //        let fileManger = NSFileManager.defaultManager()
        //        if fileManger.fileExistsAtPath(destFilepath){
        //            do {
        //                print("======================saveFileToDisk remove  destFilepath: \(destFilepath)")
        //                try fileManger.removeItemAtPath(destFilepath)
        //            } catch let error as NSError {
        //                print("======================saveFileToDisk remove  ERROR: \(error)")
        //            }
        //
        //        }
        
        
        //获取后缀名
        let desName = originUrl.lastPathComponent!
        let srcName = location.lastPathComponent!
        print("saveFileToDisk srcName = \(srcName) desName = \(desName)")
        
        
        //删除已存在的文件
        let fileManger = NSFileManager.defaultManager()
        
        let docUrls =  fileManger.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
        let destUrl = docUrls[0].URLByAppendingPathComponent(desName)
        print("saveFileToDisk destUrl.path!: \(destUrl.path!)")
        
        do {
            try fileManger.removeItemAtURL(destUrl)
        } catch let error as NSError {
            print("=======================saveFileToDisk removeItemAtURL  ERROR: \(error)")
        }
        do {
            //try fileManger.copyItemAtPath(srcPath, toPath: destFilepath)
            try fileManger.copyItemAtURL(location, toURL: destUrl)
            
        } catch let error as NSError {
            print("=======================saveFileToDisk copyItemAtURL  ERROR: \(error)")
        }
        
        if fileManger.fileExistsAtPath(destUrl.path!){
            print("saveFileToDisk move item ok: \(getFileSizeFromPath(destUrl.path!))")
        }
        
        
    }
    
    
    
    func getFileSizeFromPath(path:String)->Int{
        let file = fopen(path,"r")
        
        var fileSizeBytes = 0;
        
        if(file.hashValue > 0){
            fseek(file, 0, SEEK_END);
            fileSizeBytes = ftell(file);
            fseek(file, 0, SEEK_SET);
            fclose(file);
        }
        return fileSizeBytes;
    }
}

//证书验证问题:如要果返回fals,由系统处理的话,https的下载会被取消
extension JPCDownloadHelper:NSURLConnectionDelegate{
    func connection(connection: NSURLConnection, canAuthenticateAgainstProtectionSpace protectionSpace: NSURLProtectionSpace) -> Bool {
        print("=======================canAuthenticateAgainstProtectionSpace ")
        return true
    }
    
    func connection(connection: NSURLConnection, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
        print("=======================didReceiveAuthenticationChallenge ")
        //[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
        challenge.sender?.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!), forAuthenticationChallenge: challenge)
    }
}
返回顶部