美文网首页
猫猫学Swift之下载-断点续传

猫猫学Swift之下载-断点续传

作者: 翟乃玉 | 来源:发表于2018-04-09 16:39 被阅读175次

    猫猫分享,必须精品

    原创文章,欢迎转载。转载请注明:翟乃玉的博客
    地址:http://www.jianshu.com/notebooks/4236923/latest

    下载-断点续传

    通过URLSession进行下载,通过OutputStream写入文件,通过URLSessionDataTask来控制下载的继续暂停取消等操作

    一:下载过程

    1:一次完整的下载流程

    1:创建request,session

    //request
    var request = URLRequest(url: url, cachePolicy:URLRequest.CachePolicy.reloadIgnoringLocalCacheData, 
    timeoutInterval: 0)
    
    //session
    let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main)
    

    2:设置下载偏移量 :offset

    request.setValue(String(format: "bytes=%lld-", offset), forHTTPHeaderField: "Range")
    

    3:根据request从会话session中拿到URLSessionDataTask任务,并且继续执行

    self.dataTask = self.session.dataTask(with: request)
    self.dataTask?.resume()
    

    4:通过session的代理方法,进行数据的传输下载

    ///主要用到了三个代理方法
    
     /// 第一次接受到相应的时候调用(响应头, 并没有具体的资源内容)
     /// 通过这个方法里面系统提供的回调代码块(completionHandler) 可以控制:是继续请求, 还是取消本次请求
     func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void);
     
     /// 当用户确定, 继续接受数据的时候调用
     func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data);
     
     /// 请求完成时候调用
     func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?);
    
    

    4.1第一次接受到相应的时候,保存要下载资源的大小,然后打开输出流,开始下载数据

    //创建输出流
    let outputStream = OutputStream(toFileAtPath: self.tmpFilePath, append: true)
    //打开输出流
    outputStream.open()
    //开始下载数据
    completionHandler(.allow)
    

    如果出现异常,name就取消本次请求,重新开始下载操作

    //取消本次请求
    completionHandler(.cancel)
    //重新开始下载操作
    self.downLoad()
    

    4.2当用户确定, 继续接受数据的时候,往输出流中写数据,以及一些其他操作像:进行下载进度,下载速度的计算

    // 往输出流中写入数据
    data.withUnsafeBytes({ (p: UnsafePointer<UInt8>) -> Void in
        outputStream.write(p, maxLength: data.count)
    })
    
    //进行下载进度,下载速度的计算
    tmpSize += Int64(data.count)
    //计算一秒中的速度
    downTask.totalRead += Int64(data.count);
    let currentDate = Date()
    let time = currentDate.timeIntervalSince(downTask.lastDate)
    //当前时间和上一秒时间做对比,大于等于一秒就去计算
    if  time >= 1 {
        //计算速度
        let speed = Double(downTask.totalRead) / time
        
        //把速度转成KB或M
        downTask.speed = speed
        
        //维护变量,将计算过的清零
        downTask.totalRead = 0
        //维护变量,记录这次计算的时间
        downTask.lastDate = currentDate
        NYLog("------speed : \(speed)")
    }
        
    // 记录进度
    self.progress = 1.0 * Double(tmpSize) / Double(totalSize)
    // 每隔downLoaderConfig.progressMinReturn 秒 闭包返回一次进度
    if currentDate.timeIntervalSince(progressLastDate) > downLoaderConfig.progressMinReturn {
        self.progressClosure(self.progress,tmpSize,totalSize)
        progressLastDate = currentDate
    }
    
    

    4.3请求完成时候,成功后移动文件,关闭输出流,清理会话资源


    请求完时候成移动文件.png

    2:细节:

    1. 下载时候用到两个路径:缓存路径,临时缓存路径
    2. 下载的时候,将数据写入到临时缓存路径当中
    3. 下载完成,将临时缓存路径中下载好的文件移动到缓存路径中
    4. 如果缓存路径里面有url对应的下载文件,那就说明已经下载完成了
    5. 临时缓存是否下载完成, 通过对下载的文件大小和知道的文件大小做对比实现

    3:下载文件大小的获取

    文件的大小可以通过响应头(response)的Content-Length (或者Content-Range)或者服务器给相应的字段来获取设置.

    坑:在续传的时候响应头(response)的Content-Length是变化的, 于是在第一次拿到content-length的时候根据url用UserDefaults进行了一次缓存,然后直接用.

    ps:(这个地方可以让后台服务器给,还有的做法会给一个文件的md5,如果能有文件的md5就不需要考虑url是否更改了之类的,当然这些属于业务逻辑上的了,具体还需要根据自己的业务来进行分析)

    二:断点续传原理

    1:原理

    断点续传的工作机制,在HTTP请求头中,有一个Range的关键字,通过这个关键字可以告诉服务器返回哪些数据。
    比如:
    bytes=500-999 表示第500-第999字节
    bytes=500- 表示从第500字节往后的所有字节
    然后再根据服务器返回的数据,将得到的data数据拼接到文件后面,就可以实现断点续传了。

    2:暂停和续传

    暂停和续传网上很多都是运用了resumedata来获取已经下载的信息,但是在ios10 中需要做一些特殊处理, 这里我没有用这种方式,而是通过直接拿到本地已经下载的临时文件的大小,来作为对下一次数据的请求


    检测临时文件是否存在.png

    Ps: 这里有一个神坑

    FileManager.default.attributesOfFileSystem(forPath: )
    FileManager.default.attributesOfItem(atPath: )
    

    两个方法返回的都是[FileAttributeKey: Any] 的字典,并且第一个方法有file关键字,理所当然以为是用第一个,但真的应该用第二个

    文件大小坑.png

    三:其他

    关于NSURLSession的一些知识可以看这篇文章
    iOS中利用NSURLSession进行文件断点下载

    Demo下载地址:https://github.com/znycat/NYDownLoadLib.git

    相关文章

      网友评论

          本文标题:猫猫学Swift之下载-断点续传

          本文链接:https://www.haomeiwen.com/subject/tkvihftx.html