网上相应的问题
- 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的几种可能性
解决办法
方案一
让服务端在其他接口返回下载文件的大小,已下载的大小能获取到,这样就可以知道进度了
方案二
-
文件请求接口先发送一个
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];
-
监听已下载文件大小,通过修改全局
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]; }); }];
-
启动下载请求
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { // } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) { // 业务 }]; [downloadTask resume];
- 完成
整体代码
- (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];
}