AFN下载文件时downloadProgress回调不执行?

网上相应的问题

  • https://github.com/AFNetworking/AFNetworking/issues/3509
  • [http://www.cocoachina.com/bbs/read.php?tid=1725301]

原因

是因为服务端开启了gzip压缩传输,没有Content-Length头部信息

原因分析

无gzip压缩header

HTTP/1.1 200 OK
Date    Wed, 22 May 2019 10:43:33 GMT
Server  Apache/2.4.34 (Unix)
Last-Modified   Wed, 22 May 2019 08:01:53 GMT
ETag    "505825-5897560df9e40"
Accept-Ranges   bytes
Content-Length  5265445
Keep-Alive  timeout=5, max=100
Proxy-Connection    keep-alive

gzip压缩header

HTTP/1.1 200 OK
Date    Wed, 22 May 2019 10:41:02 GMT
Server  Apache/2.4.34 (Unix)
Last-Modified   Tue, 21 May 2019 03:34:17 GMT
ETag    "3016910-5895d86052040-gzip"
Accept-Ranges   bytes
Vary    Accept-Encoding
Content-Encoding    gzip
Keep-Alive  timeout=5, max=100
Content-Type    application/zip
Proxy-Connection    keep-alive

为什么没有content-length

content-length含义

在HTTP协议中,Content-Length用于描述HTTP消息实体的传输长度the transfer-length of the message-body。在HTTP协议中,消息实体长度和消息实体的传输长度是有区别,比如说gzip压缩下,消息实体长度是压缩前的长度,消息实体的传输长度是gzip压缩后的长度。

总结

有了Transfer-Encoding,则不能有Content-Length。参考http响应头里没有或者有content-length的几种可能性

解决办法

方案一

让服务端在其他接口返回下载文件的大小,已下载的大小能获取到,这样就可以知道进度了

方案二

  1. 文件请求接口先发送一个HEAD请求,并清空Accept-Encoding请求头字段,不接受Gzip压缩,从而获取到文件大小,如下

    NSURL *URL = [NSURL URLWithString:GETSTRING(@"http://10.36.32.32/master(9).zip")];
    
    // 构建HEAD请求
    NSMutableURLRequest *headRequest = [NSMutableURLRequest requestWithURL:URL];
    [headRequest setHTTPMethod:@"HEAD"];
    // 清空Accept-Encoding请求头字段,不接受Gzip压缩
    [headRequest setValue:@"" forHTTPHeaderField:@"Accept-Encoding"];
    
    self.uploadProgress = [NSProgress new];
    NSURLSessionDataTask *headTask = [manager dataTaskWithRequest:headRequest
                                      uploadProgress:nil
                                      downloadProgress:nil
                                      completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) 
    {
      // 设置总数
      self.uploadProgress.totalUnitCount = response.expectedContentLength;
    }];
    [headTask resume];
    
  2. 监听已下载文件大小,通过修改全局NSProgress对象completedUnitCount来修改进度,并通知UI修改进度

    [manager setDownloadTaskDidWriteDataBlock:^(
      NSURLSession * _Nonnull session,                                                                      
      NSURLSessionDownloadTask * _Nonnull downloadTask,
      int64_t bytesWritten, 
      int64_t totalBytesWritten, 
      int64_t totalBytesExpectedToWrite) 
     {
             self.uploadProgress.completedUnitCount = totalBytesWritten;
                 dispatch_async(dispatch_get_main_queue(), ^{                                                     
              //Update the progress view
              [self.upView setProgress:self.uploadProgress];
                 });
      }];
    
  3. 启动下载请求

     NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request   
         progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response)                                                                                                       {
             // 
         } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
         // 业务
        }];
     [downloadTask resume];
    
  1. 完成

整体代码

- (void)downloadFile {
  NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration     defaultSessionConfiguration];
  // 构建SessionManager
  AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

  NSURL *URL = [NSURL URLWithString:GETSTRING(@"http://10.36.32.32/master(9).zip")];

  // 构建HEAD请求
  NSMutableURLRequest *headRequest = [NSMutableURLRequest requestWithURL:URL];
  [headRequest setHTTPMethod:@"HEAD"];
  // 清空Accept-Encoding请求头字段,不接受Gzip压缩
  [headRequest setValue:@"" forHTTPHeaderField:@"Accept-Encoding"];

    self.uploadProgress = [NSProgress new];
    NSURLSessionDataTask *headTask = [manager dataTaskWithRequest:headRequest
                                  uploadProgress:nil
                                  downloadProgress:nil
                                  completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
  // 设置总数
  self.uploadProgress.totalUnitCount = response.expectedContentLength;

                                    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];                                      
    [manager setDownloadTaskDidWriteDataBlock:^(NSURLSession * _Nonnull session,                                                                            NSURLSessionDownloadTask * _Nonnull downloadTask, int64_t                                                                                           bytesWritten, int64_t totalBytesWritten, int64_t                                                                                            totalBytesExpectedToWrite) {
        NSLog(@"----aa%li,%li",totalBytesExpectedToWrite,totalBytesWritten);
            self.uploadProgress.completedUnitCount = totalBytesWritten;
                dispatch_async(dispatch_get_main_queue(), ^{                                                     
          //Update the progress view
          [self.upView setProgress:self.uploadProgress];
                });
  }];

  NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request     
        progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response)                                                                                                       {
       //拼接缓存目录
        NSString *downloadDir = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:Report_Download_Word_FileName];
        //打开文件管理器
        NSFileManager *fileManager = [NSFileManager defaultManager];
        //创建Download目录
        [fileManager createDirectoryAtPath:downloadDir withIntermediateDirectories:YES attributes:nil error:nil];
        //拼接文件路径
        NSString *filePath = [downloadDir stringByAppendingPathComponent:response.suggestedFilename];
        //返回文件位置的URL路径
        return [NSURL fileURLWithPath:filePath];                                                                                        } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
        // 业务
    }];
   [downloadTask resume];
    }];
  [headTask resume];
}

你可能感兴趣的:(AFN下载文件时downloadProgress回调不执行?)