Http文件上传进度为什么不准

作者: MrPeak | 来源:发表于2016-11-18 10:13 被阅读212次

    做过客户端文件上传的同学会明白,基于HTTP的文件上传并没有看起来那么简单。按我过去经验,至少有两块工作会比看上去要麻烦一些,第一个是断点续传,第二个是进度展示。断点续传想要优化的好要花不少力气,后面有机会再写,这篇先看上传进度的问题。

    首先说现象:我们调用第三方http framework上传文件的时候,都会有API回调告诉我们上传的具体进度,这个进度值都是不准的。

    比如下面代码使用alamofire(使用AFNetworking也一样)标准API上传一个图片到服务器:

    func doUpload() {
          let fileURL = Bundle.main.url(forResource: "test", withExtension: "png")
          var sentUnitCount: Int64 = 0
    
          var start: CFTimeInterval = 0
          var end: CFTimeInterval = 0
          var cur: CFTimeInterval = CACurrentMediaTime()
          
          Alamofire.upload(fileURL!, to: "https://httpbin.org/post")
              .uploadProgress { progress in
                  print("Upload Progress: \(progress.fractionCompleted), sent bytes: \(progress.completedUnitCount-sentUnitCount), totoal sent bytes: \(progress.completedUnitCount/1024)")
                  sentUnitCount = progress.completedUnitCount
    
                  if progress.fractionCompleted == 1 {
                      start = CACurrentMediaTime();
                  }
    
                  let now = CACurrentMediaTime()
                  print("elapsed: \(now - cur)")
                  cur = now
              }
          .response { (rsp) in
              end = CACurrentMediaTime();
              print("\(end - start)")
          }
    }
    

    上面的代码会上传一个大约20KB的图片到目标服务器,如果执行代码,你会发现uploadProgress会在一瞬间到达100%,无论设备的网络是快还是慢(大小为100KB的图片也一样)。我们当然知道一个20KB的图片不可能瞬间抵达服务器,那么这个进度到底准不准,问题在哪呢?

    要理解这个uploadProgress的含义,需要理解tcp协议发送数据的方式,略去一些协议的细节,大致的模型可以用下图表示:

    我们的客户端(TCP的发送方)会有一个send buffer,这个send buffer会缓存等待发送的数据,alamofire等第三方库会先将数据持续写入这个send buffer,每写一次就会调用一次uploadProgress,实际上这时候数据还老老实实待在send buffer当中,并没有抵达服务器,所以会有上面一调用upload函数进度瞬间到100%的现象,这时候如果你根据progress展示进度条,用户就会发现进度条瞬间跳到100%,之后一直卡住,直到send buffer当中的数据全部真正抵达Server并被Server Ack之后,才会进入response回调。

    我拿自己的iPhone6测试1MB大小的文件,alamofire每写4KB数据会进入一次uploadProgress回调,一直写到大概120KB左右才停住,等这120KB传输完成之后,再啪啪啪写入后面的100多KB。

    所以根据上面的分析,如果你对文件上传不做任何优化,会有两个奇怪的现象:

    1. 几十KB的小文件会在一瞬间progress到100%。
    2. 进度跳到100%之后,会卡住几秒(时长因网络状况而异)再进入response回调。

    这种场景当然需要优化,怎么优化呢?有套路的,用个假的进度条就可以了。

    解决方案

    先不要管上传的进度,做个步调优雅的进度动画,慢慢儿的先滑到50%,看下 if 这时候上传进度有50%,继续往前滑到75%,else 没到50%就继续等上传进度,如此往复直达100%。为了避免100%之后还要等最后send buffer清空,可以在99%的位置等一等。这种假进度条的套路在浏览器加载网页的时候也会用到,使用过微信打开网页等加载完成的就能明白。这个套路广泛的应用于无法获取真实进度,却不得不给产品经理或者用户一个交代的场景。

    这个上传进度的坑你踩过没呢?

    课后作业:根据上面的分析,下载的进度准不准呢?

    相关文章

      网友评论

      本文标题:Http文件上传进度为什么不准

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