背景
文件下载需要支持断点续传即杀死应用重新启动后未下载完的文件仍要继续下载
当前主要使用NSURLSessionDownloadTask完成任务文件下载。
坑 & 解决方案
正常情况下只需要拿到上次缓存下来的数据,用系统方法如下:
// resumeData指上次缓存的数据
NSURLSessionDownloadTask *downloadTask = [_session downloadTaskWithResumeData:model.resumeData];
但是在iOS 10.0到10.1上有问题。也就是说通过上述无法找到task。经过研究搜索可通过以下方法寻找解决为了方便,增加NSURLSession分类:
NSURLSessionDownloadTask *downloadTaskdownloadTask = [_session downloadTaskWithCorrectResumeData:model.resumeData];
分类 NSURLSession
- 用于修复iOS 10.0、10.1系统暂停后继续下载错误问题,可参考resume_nsurlsession_ios
- (NSURLSessionDownloadTask *)downloadTaskWithCorrectResumeData:(NSData *)resumeData
{
NSData *data = [self getCorrectResumeDataWithData:resumeData];
data = data ? data : resumeData;
NSURLSessionDownloadTask *task = [self downloadTaskWithResumeData:data];
NSMutableDictionary *resumeDic = [self getResumeDictionaryWithData:data];
if (resumeDic) {
if (!task.originalRequest) {
NSData *originalReqData = resumeDic[resumeOriginalRequest];
NSURLRequest *originalRequest = [NSKeyedUnarchiver unarchiveObjectWithData:originalReqData];
if (originalRequest) [task setValue:originalRequest forKey:@"originalRequest"];
}
if (!task.currentRequest) {
NSData *currentReqData = resumeDic[resumeCurrentRequest];
NSURLRequest *currentRequest = [NSKeyedUnarchiver unarchiveObjectWithData:currentReqData];
if (currentRequest) [task setValue:currentRequest forKey:@"currentRequest"];
}
}
return task;
}
- (NSData *)getCorrectResumeDataWithData:(NSData *)data
{
if (!data) return nil;
NSMutableDictionary *resumeDictionary = [self getResumeDictionaryWithData:data];
if (!resumeDictionary) return nil;
resumeDictionary[resumeCurrentRequest] = [self getCorrectRequestDataWithData:resumeDictionary[resumeCurrentRequest]];
resumeDictionary[resumeOriginalRequest] = [self getCorrectRequestDataWithData:resumeDictionary[resumeOriginalRequest]];
return [NSPropertyListSerialization dataWithPropertyList:resumeDictionary format:NSPropertyListXMLFormat_v1_0 options:0 error:nil];
}
- (NSMutableDictionary *)getResumeDictionaryWithData:(NSData *)data
{
return [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListMutableContainersAndLeaves format:nil error:nil];
}
- (NSData *)getCorrectRequestDataWithData:(NSData *)data
{
if (!data) return nil;
if ([NSKeyedUnarchiver unarchiveObjectWithData:data]) return data;
NSMutableDictionary *archive = [[NSPropertyListSerialization propertyListWithData:data options:NSPropertyListMutableContainersAndLeaves format:nil error:nil] mutableCopy];
if (!archive) return nil;
NSInteger i = 0;
id objectss = archive[@"$objects"];
while ([objectss[1] objectForKey:[NSString stringWithFormat:@"$%ld", i]]) {
i++;
}
NSInteger j = 0;
while ([archive[@"$objects"][1] objectForKey:[NSString stringWithFormat:@"__nsurlrequest_proto_prop_obj_%ld", j]]) {
NSMutableArray *array = archive[@"$objects"];
NSMutableDictionary *dic = array[1];
id obj = [dic objectForKey:[NSString stringWithFormat:@"__nsurlrequest_proto_prop_obj_%ld", j]];
if (obj) {
[dic setValue:obj forKey:[NSString stringWithFormat:@"$%ld", i + j]];
[dic removeObjectForKey:[NSString stringWithFormat:@"__nsurlrequest_proto_prop_obj_%ld", j]];
[array replaceObjectAtIndex:1 withObject:dic];
archive[@"$objects"] = array;
}
j++;
}
if ([archive[@"$objects"][1] objectForKey:@"__nsurlrequest_proto_props"]) {
NSMutableArray *array = archive[@"$objects"];
NSMutableDictionary *dic = array[1];
id obj = [dic objectForKey:@"__nsurlrequest_proto_props"];
if (obj) {
[dic setValue:obj forKey:[NSString stringWithFormat:@"$%ld", i + j]];
[dic removeObjectForKey:@"__nsurlrequest_proto_props"];
[array replaceObjectAtIndex:1 withObject:dic];
archive[@"$objects"] = array;
}
}
if ([archive[@"$top"] objectForKey:@"NSKeyedArchiveRootObjectKey"]) {
[archive[@"$top"] setObject:archive[@"$top"][@"NSKeyedArchiveRootObjectKey"] forKey: NSKeyedArchiveRootObjectKey];
[archive[@"$top"] removeObjectForKey:@"NSKeyedArchiveRootObjectKey"];
}
return [NSPropertyListSerialization dataWithPropertyList:archive format:NSPropertyListBinaryFormat_v1_0 options:0 error:nil];
}