使用NSURLSession程序退出后继续下载

iOS 7发布了 NSURLSession,在Xcode7.x里面 NSURLConnection有效范围是iOS 2 - iOS 9,估计在iOS 10也将继续支持,但是NSURLSessison从iOS 7开始支持的,所以还是尽快替换吧。我在整理的时候发现NSURLSession下载文件的接口如下:

- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;

手动点暂停后保存下载的data,继续下载,接口代码如下:

//NSURLSessionDownloadTask 手动暂停
- (void)cancelByProducingResumeData:(void (^)(NSData * __nullable resumeData))completionHandler; //继续下载 - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;

可是当我们遇到程序退出再加载进来依然需要保持断点下载的需求我们该怎么做?“这个功能实现不了!“,在我们iOS程序猿这没有实现不了的需求,OK,我们来分析下如何实现。
看NSURLSession的头文件,只有downloadTaskWithResumeData这个函数,参数NSData,那就简答明了了,很明显data里面肯定会包涵下载地址,下载了多少字节等信息,直接打印出cancelByProducingResumeData函数返回的resumeData:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSURLSessionDownloadURL</key>
    <string>http://gdown.baidu.com/data/wisegame/35c2db2914b058bc/QQ_312.apk</string>
    <key>NSURLSessionResumeBytesReceived</key>
    <integer>2133255</integer>
    <key>NSURLSessionResumeCurrentRequest</key>
    <data>
    ******
    </data>
    <key>NSURLSessionResumeEntityTag</key>
    <string>cd44ff1535c2db2914b058bcc6b7858d</string>
    <key>NSURLSessionResumeInfoTempFileName</key>
    <string>CFNetworkDownload_QxBXCG.tmp</string>
    <key>NSURLSessionResumeInfoVersion</key>
    <integer>2</integer>
    <key>NSURLSessionResumeOriginalRequest</key>
    <data>
    ******
    </data>
    <key>NSURLSessionResumeServerDownloadDate</key>
    <string>Thu, 10 Dec 2015 02:45:00 GMT</string>
</dict>
</plist>

从日志里面看到其实就是一个plist文件,我们只需要构造一个plist就ok了。

    NSMutableDictionary *resumeDataDict = [NSMutableDictionary dictionary];
    NSMutableURLRequest *newResumeRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
    [newResumeRequest addValue:[NSString stringWithFormat:@"bytes=%ld-",data.length] forHTTPHeaderField:@"Range"];
    NSData *newResumeRequestData = [NSKeyedArchiver archivedDataWithRootObject:newResumeRequest];
    [resumeDataDict setObject:[NSNumber numberWithInteger:data.length]forKey:@"NSURLSessionResumeBytesReceived"];
    [resumeDataDict setObject:newResumeRequestData forKey:@"NSURLSessionResumeCurrentRequest"];
    [resumeDataDict setObject:[path lastPathComponent]forKey:@"NSURLSessionResumeInfoTempFileName"];
    NSData *resumeData = [NSPropertyListSerialization dataWithPropertyList:resumeDataDict format:NSPropertyListBinaryFormat_v1_0 options:0 error:nil];

结合AFNetWorking3.0解决tmp目录的临时文件地址,我们可以通过runtime接口找到内部属性,找到临时文件的地址属性 。

    //拉取属性
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList([downloadTask class], &outCount);
    for (i = 0; i<outCount; i++)
    {
        objc_property_t property = properties[i];
        const char* char_f =property_getName(property);
        NSString *propertyName = [NSString stringWithUTF8String:char_f];

        if ([@"downloadFile" isEqualToString:propertyName])
        {
            id propertyValue = [downloadTask valueForKey:(NSString *)propertyName];
            unsigned int downloadFileoutCount, downloadFileIndex;
            objc_property_t *downloadFileproperties = class_copyPropertyList([propertyValue class], &downloadFileoutCount);
            for (downloadFileIndex = 0; downloadFileIndex < downloadFileoutCount; downloadFileIndex++)
            {
                objc_property_t downloadFileproperty = downloadFileproperties[downloadFileIndex];
                const char* downloadFilechar_f =property_getName(downloadFileproperty);
                NSString *downloadFilepropertyName = [NSString stringWithUTF8String:downloadFilechar_f];
                if([@"path" isEqualToString:downloadFilepropertyName])
                {
                    id downloadFilepropertyValue = [propertyValue valueForKey:(NSString *)downloadFilepropertyName];
                    if(!contiueDownload)
                    {
                        //保存文件临时下载任务
                        SaveTmpFileName(afnetClientRequest.url, [downloadFilepropertyValue lastPathComponent]);
                    }

                    break;
                }
            }
            free(downloadFileproperties);
        }
        else
        {
            continue;
        }
    }
    free(properties);
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler

你可能感兴趣的:(ios)