用NSURLConnection封装一个断点续传后台下载的轻量级下载工具

###摘要

  • 本文讲述,用NSURLConnection封装一个断点下载,支持后台下载的完整的下载工具。
    ###效果图
    效果图

###用法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[[FGGDownloadManager shredManager] downloadWithUrlString:model.url toPath:model.destinationPath process:^(float progress, NSString *sizeString, NSString *speedString) {
//更新进度条的进度值
weakCell.progressView.progress=progress;
//更新进度值文字
weakCell.progressLabel.text=[NSString stringWithFormat:@"%.2f%%",progress*100];
//更新文件已下载的大小
weakCell.sizeLabel.text=sizeString;
//显示网速
weakCell.speedLabel.text=speedString;
if(speedString)
weakCell.speedLabel.hidden=NO;

} completion:^{
[sender setTitle:@"完成" forState:UIControlStateNormal];
sender.enabled=NO;
weakCell.speedLabel.hidden=YES;
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"提示" message:[NSString stringWithFormat:@"%@下载完成✅",model.name] delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
[alert show];

} failure:^(NSError *error) {
[[FGGDownloadManager shredManager] cancelDownloadTask:model.url];
[sender setTitle:@"恢复" forState:UIControlStateNormal];
weakCell.speedLabel.hidden=YES;
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"Error" message:error.localizedDescription delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
[alert show];
}];

###思路

  • 搞一个下载类,负责一个下载任务;
  • 搞一个下载管理类,负责下载队列;
  • 在程序进入后台时,开启后台下载;
  • 在程序被终结时取消所有下载并保存下载进度;
  • 当程序启动时,加载上次下载进度;

###下载类 FGGDownloader.h
FGGDownloader遵循NSURLConnection的一些协议:
@interface FGGDownloader : NSObject<NSURLConnectionDataDelegate,NSURLConnectionDelegate>
接下来定义三个代码块:

1
2
3
typedef void (^ProcessHandle)(float progress,NSString *sizeString,NSString *speedString);
typedef void (^CompletionHandle)();
typedef void (^FailureHandle)(NSError *error);

然后声明三个只读属性:

1
2
3
4
5
6
//下载过程中回调的代码块,会多次调用
@property(nonatomic,copy,readonly)ProcessHandle process;
//下载完成回调的代码块
@property(nonatomic,copy,readonly)CompletionHandle completion;
//下载失败的回调代码块
@property(nonatomic,copy,readonly)FailureHandle failure;

写一个快速实例的类方法:

1
+(instancetype)downloader;

搞一个下载的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 断点下载
*
* @param urlString 下载的链接
* @param destinationPath 下载的文件的保存路径
* @param process 下载过程中回调的代码块,会多次调用
* @param completion 下载完成回调的代码块
* @param failure 下载失败的回调代码块
*/
-(void)downloadWithUrlString:(NSString *)urlString
toPath:(NSString *)destinationPath
process:(ProcessHandle)process
completion:(CompletionHandle)completion
failure:(FailureHandle)failure;

搞一个取消下载的方法:

1
2
3
4
/**
* 取消下载
*/
-(void)cancel;

程序启动的时候要加载上一次的下载进度:

1
2
3
4
/**
* 获取上一次的下载进度
*/
+(float)lastProgress:(NSString *)url;

需要在界面上显示文件下载了的大小,文件总大小:

1
2
3
4
5
/**获取文件已下载的大小和总大小,格式为:已经下载的大小/文件总大小,如:12.00M/100.00M。
*
* @param url 下载链接
*/
+(NSString *)filesSize:(NSString *)url;

搞三个通知,因为下载管理类要用:

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 下载完成的通知名
*/
static NSString *const FGGDownloadTaskDidFinishDownloadingNotification=@"FGGDownloadTaskDidFinishDownloadingNotification";
/**
* 系统存储空间不足的通知名
*/
static NSString *const FGGInsufficientSystemSpaceNotification=@"FGGInsufficientSystemSpaceNotification";
/**
* 下载进度改变的通知
*/
static NSString *const FGGProgressDidChangeNotificaiton=@"FGGProgressDidChangeNotificaiton";

###下载类的实现 FGGDownloader.m
大文件下载,要遵循低内存占用,因此我们要有一个文件读写句柄:NSFileHandle,以及一些储放下载位置、下载路径、链接等一些东西的成员变量。


说明一下,_timer是为了获取网速近似值,没0.5秒计算文件增长的大小,取网速平均值.
这里有个假设:假设文件读写不占用时间,而在这0.5秒内文件的增量直接反应网速。

1
2
3
4
5
6
7
8
9
10
@implementation FGGDownloader{

NSString *_url_string;
NSString *_destination_path;
NSFileHandle *_writeHandle;
NSURLConnection *_con;
NSUInteger _lastSize;
NSUInteger _growth;
NSTimer *_timer;
}

快速实例化的类方法:

1
2
3
4
+(instancetype)downloader{

return [[[self class] alloc]init];
}

初始化方法:

1
2
3
4
5
6
7
8
9
-(instancetype)init{

if(self=[super init]){

//每0.5秒计算一次文件大小增加部分的尺寸
_timer=[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(getGrowthSize) userInfo:nil repeats:YES];
}
return self;
}

计算文件没0.5秒的增量,以计算网速近似值

1
2
3
4
5
6
7
//计算一次文件大小增加部分的尺寸,以计算网速近似值
-(void)getGrowthSize
{
NSUInteger size=[[[[NSFileManager defaultManager] attributesOfItemAtPath:_destination_path error:nil] objectForKey:NSFileSize] integerValue];
_growth=size-_lastSize;
_lastSize=size;
}

关键方法,下载接口方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 断点下载
*
* @param urlString 下载的链接
* @param destinationPath 下载的文件的保存路径
* @param process 下载过程中回调的代码块,会多次调用
* @param completion 下载完成回调的代码块
* @param failure 下载失败的回调代码块
*/
-(void)downloadWithUrlString:(NSString *)urlString toPath:(NSString *)destinationPath process:(ProcessHandle)process completion:(CompletionHandle)completion failure:(FailureHandle)failure{

if(urlString&&destinationPath){

_url_string=urlString;
_destination_path=destinationPath;
_process=process;
_completion=completion;
_failure=failure;

NSURL *url=[NSURL URLWithString:urlString];
NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:url];
NSFileManager *fileManager=[NSFileManager defaultManager];
BOOL fileExist=[fileManager fileExistsAtPath:destinationPath];
if(fileExist){

NSUInteger length=[[[fileManager attributesOfItemAtPath:destinationPath error:nil] objectForKey:NSFileSize] integerValue];
NSString *rangeString=[NSString stringWithFormat:@"bytes=%ld-",length];
[request setValue:rangeString forHTTPHeaderField:@"Range"];
}
_con=[NSURLConnection connectionWithRequest:request delegate:self];
}
}

取消下载:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 取消下载
*/
-(void)cancel{

[self.con cancel];
self.con=nil;
if(_timer){

[_timer invalidate];
_timer=nil;
}
}

程序每次启动都要获取上次的下载进度:

1
2
3
4
5
6
7
8
9
/**
* 获取上一次的下载进度
*/
+(float)lastProgress:(NSString *)url{

if(url)
return [[NSUserDefaults standardUserDefaults]floatForKey:[NSString stringWithFormat:@"%@progress",url]];
return 0.0;
}

通过下载链接url获取文件的大小信息(当前大小/总大小 组成的字符串)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**获取文件已下载的大小和总大小,格式为:已经下载的大小/文件总大小,如:12.00M/100.00M
*/
+(NSString *)filesSize:(NSString *)url{

NSString *totalLebgthKey=[NSString stringWithFormat:@"%@totalLength",url];
NSUserDefaults *usd=[NSUserDefaults standardUserDefaults];
NSUInteger totalLength=[usd integerForKey:totalLebgthKey];
if(totalLength==0){

return @"0.00K/0.00K";
}
NSString *progressKey=[NSString stringWithFormat:@"%@progress",url];
float progress=[[NSUserDefaults standardUserDefaults] floatForKey:progressKey];
NSUInteger currentLength=progress*totalLength;

NSString *currentSize=[self convertSize:currentLength];
NSString *totalSize=[self convertSize:totalLength];
return [NSString stringWithFormat:@"%@/%@",currentSize,totalSize];
}

下面是个工具方法,把文件的长度(字节)转成字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 计算缓存的占用存储大小
*
* @prama length 文件大小
*/
+(NSString *)convertSize:(NSUInteger)length
{
if(length<1024)
return [NSString stringWithFormat:@"%ldB",(NSUInteger)length];
else if(length>=1024&&length<1024*1024)
return [NSString stringWithFormat:@"%.0fK",(float)length/1024];
else if(length >=1024*1024&&length<1024*1024*1024)
return [NSString stringWithFormat:@"%.1fM",(float)length/(1024*1024)];
else
return [NSString stringWithFormat:@"%.1fG",(float)length/(1024*1024*1024)];
}

下载过程可能会存储空间不足,若不做处理,会导致crash,因此每次句柄写文件时,都要判断存储空间是否足够:

1
2
3
4
5
6
7
8
9
10
11
/**
* 获取系统可用存储空间
*
* @return 系统空用存储空间,单位:字节
*/
-(NSUInteger)systemFreeSpace{

NSString *docPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSDictionary *dict=[[NSFileManager defaultManager] attributesOfFileSystemForPath:docPath error:nil];
return [[dict objectForKey:NSFileSystemFreeSize] integerValue];
}

###下面是NSURLConnect的代理和数据源
下载失败,回调error block:

1
2
3
4
5
6
7
8
9
#pragma mark - NSURLConnection
/**
* 下载失败
*/
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{

if(_failure)
_failure(error);
}

在接受到响应请求的代理方法中存储文件中大小,以及初始化文件读写句柄_writeHandle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 接收到响应请求
*/
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{

NSString *key=[NSString stringWithFormat:@"%@totalLength",_url_string];
NSUserDefaults *usd=[NSUserDefaults standardUserDefaults];
NSUInteger totalLength=[usd integerForKey:key];
if(totalLength==0){

[usd setInteger:response.expectedContentLength forKey:key];
[usd synchronize];
}
NSFileManager *fileManager=[NSFileManager defaultManager];
BOOL fileExist=[fileManager fileExistsAtPath:_destination_path];
if(!fileExist)
[fileManager createFileAtPath:_destination_path contents:nil attributes:nil];
_writeHandle=[NSFileHandle fileHandleForWritingAtPath:_destination_path];
}

下载过程的接收数据的回调,处理计算网速、下载进度、文件大小信息,存档当前大小等操作,最后回调网速,文件大小/总大小字符串,下载进度信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* 下载过程,会多次调用
*/
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{

[_writeHandle seekToEndOfFile];

NSUInteger freeSpace=[self systemFreeSpace];
if(freeSpace<1024*1024*20){

UIAlertController *alertController=[UIAlertController alertControllerWithTitle:@"提示" message:@"系统可用存储空间不足20M" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *confirm=[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
[alertController addAction:confirm];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
//发送系统存储空间不足的通知,用户可自行注册该通知,收到通知时,暂停下载,并更新界面
[[NSNotificationCenter defaultCenter] postNotificationName:FGGInsufficientSystemSpaceNotification object:nil userInfo:@{@"urlString":_url_string}];
return;
}
[_writeHandle writeData:data];
NSUInteger length=[[[[NSFileManager defaultManager] attributesOfItemAtPath:_destination_path error:nil] objectForKey:NSFileSize] integerValue];
NSString *key=[NSString stringWithFormat:@"%@totalLength",_url_string];
NSUInteger totalLength=[[NSUserDefaults standardUserDefaults] integerForKey:key];

//计算下载进度
float progress=(float)length/totalLength;

[[NSUserDefaults standardUserDefaults]setFloat:progress forKey:[NSString stringWithFormat:@"%@progress",_url_string]];
[[NSUserDefaults standardUserDefaults] synchronize];

//获取文件大小,格式为:格式为:已经下载的大小/文件总大小,如:12.00M/100.00M
NSString *sizeString=[FGGDownloader filesSize:_url_string];

//发送进度改变的通知(一般情况下不需要用到,只有在触发下载与显示下载进度在不同界面的时候才会用到)
NSDictionary *userInfo=@{@"url":_url_string,@"progress":@(progress),@"sizeString":sizeString};
[[NSNotificationCenter defaultCenter] postNotificationName:FGGProgressDidChangeNotificaiton object:nil userInfo:userInfo];

//计算网速
NSString *speedString=@"0.00Kb/s";
NSString *growString=[FGGDownloader convertSize:_growth*(1.0/0.1)];
speedString=[NSString stringWithFormat:@"%@/s",growString];

//回调下载过程中的代码块
if(_process)
_process(progress,sizeString,speedString);
}

下载完成,发送通知,回调下载完成的代码块:

1
2
3
4
5
6
7
8
9
/**
* 下载完成
*/
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{

[[NSNotificationCenter defaultCenter] postNotificationName:FGGDownloadTaskDidFinishDownloadingNotification object:nil userInfo:@{@"urlString":_url_string}];
if(_completion)
_completion();
}

###下载管理类 FGGDownloadManager.h
首先要导入下载类:#import "FGGDownloader.h"
单例接口

1
+(instancetype)shredManager;

同样有添加下载任务的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 断点下载
*
* @param urlString 下载的链接
* @param destinationPath 下载的文件的保存路径
* @param process 下载过程中回调的代码块,会多次调用
* @param completion 下载完成回调的代码块
* @param failure 下载失败的回调代码块
*/
-(void)downloadWithUrlString:(NSString *)urlString
toPath:(NSString *)destinationPath
process:(ProcessHandle)process
completion:(CompletionHandle)completion
failure:(FailureHandle)failure;

管理类自然可以暂停指定的某个下载,根据某个下载链接url,暂停某个下载

1
2
3
4
5
6
/**
* 暂停下载
*
* @param url 下载的链接
*/
-(void)cancelDownloadTask:(NSString *)url;

管理类可以暂停所有下载任务:

1
2
3
4
/**
* 暂停所有下载
*/
-(void)cancelAllTasks;

管理类可以彻底移除某个下载任务:

1
2
3
4
5
6
7
/**
* 彻底移除下载任务
*
* @param url 下载链接
* @param path 文件路径
*/
-(void)removeForUrl:(NSString *)url file:(NSString *)path;

当然管理类可以根据下载链接url获取上一次的下载进度:

1
2
3
4
5
6
7
8
/**
* 获取上一次的下载进度
*
* @param url 下载链接
*
* @return 下载进度
*/
-(float)lastProgress:(NSString *)url;

以及获取文件大小信息:

1
2
3
4
5
6
7
8
/**
* 获取文件已下载的大小和总大小,格式为:已经下载的大小/文件总大小,如:12.00M/100.00M。
*
* @param url 下载链接
*
* @return 有文件大小及总大小组成的字符串
*/
-(NSString *)filesSize:(NSString *)url;

###管理类的实现 FGGDownloadManager.m

  • 在这里,我们搞了一个队列,设置了同时最多允许多少个下载任务。
  • 超过最大下载任务后,添加下载任务将会被添加进入下载任务队列,处于等待模式。
  • 当当前正在进行的下载任务数小于允许最大同时下载任务数时,队列中的一个下载任务出列(遵循先入列的先出列)。

根据逻辑,我定制了以下成员变量

1
2
3
4
5
6
7
8
9
10
11
12
@implementation FGGDownloadManager{

NSMutableDictionary *_taskDict;
/**
* 排队对列
*/
NSMutableArray *_queue;
/**
* 后台进程id
*/
UIBackgroundTaskIdentifier _backgroudTaskId;
}

单例方法:

1
2
3
4
5
6
7
8
+(instancetype)shredManager
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mgr=[[FGGDownloadManager alloc]init];
});
return mgr;
}

初始化的时候,注册app进入后台,app会到前台,被终结等,以及系统内存不足的通知:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-(instancetype)init{

if(self=[super init]){

_taskDict=[NSMutableDictionary dictionary];
_queue=[NSMutableArray array];
_backgroudTaskId=UIBackgroundTaskInvalid;
//注册系统内存不足的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(systemSpaceInsufficient:) name:FGGInsufficientSystemSpaceNotification object:nil];
//注册程序下载完成的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadTaskDidFinishDownloading:) name:FGGDownloadTaskDidFinishDownloadingNotification object:nil];
//注册程序即将失去焦点的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadTaskWillResign:) name:UIApplicationWillResignActiveNotification object:nil];
//注册程序获得焦点的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadTaskDidBecomActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
//注册程序即将被终结的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadTaskWillBeTerminate:) name:UIApplicationWillTerminateNotification object:nil];

}
return self;
}

收到系统内存不足的通知时,取消下载:

1
2
3
4
5
6
7
8
9
10
/**
* 收到系统存储空间不足的通知调用的方法
*
* @param sender 系统存储空间不足的通知
*/
-(void)systemSpaceInsufficient:(NSNotification *)sender{

NSString *urlString=[sender.userInfo objectForKey:@"urlString"];
[[FGGDownloadManager shredManager] cancelDownloadTask:urlString];
}

程序即将失去焦点,开启后台:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 收到程序即将失去焦点的通知,开启后台运行
*
* @param sender 通知
*/
-(void)downloadTaskWillResign:(NSNotification *)sender{

if(_taskDict.count>0){

_backgroudTaskId=[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

}];
}
}

程序重新获得焦点时,关闭后台:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 收到程序重新得到焦点的通知,关闭后台
*
* @param sender 通知
*/
-(void)downloadTaskDidBecomActive:(NSNotification *)sender{

if(_backgroudTaskId!=UIBackgroundTaskInvalid){

[[UIApplication sharedApplication] endBackgroundTask:_backgroudTaskId];
_backgroudTaskId=UIBackgroundTaskInvalid;
}
}

程序即将被终结时,取消所有下载任务:

1
2
3
4
5
6
7
8
9
/**
* 程序将要结束时,取消下载
*
* @param sender 通知
*/
-(void)downloadTaskWillBeTerminate:(NSNotification *)sender{

[[FGGDownloadManager shredManager] cancelAllTasks];
}

收到下载完成的通知时,从排队队列中取出一个任务出列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 下载完成通知调用的方法
*
* @param sender 通知
*/
-(void)downloadTaskDidFinishDownloading:(NSNotification *)sender{

//下载完成后,从任务列表中移除下载任务,若总任务数小于最大同时下载任务数,
//则从排队对列中取出一个任务,进入下载
NSString *urlString=[sender.userInfo objectForKey:@"urlString"];
[_taskDict removeObjectForKey:urlString];
if(_taskDict.count<kFGGDwonloadMaxTaskCount){

if(_queue.count>0){

NSDictionary *first=[_queue objectAtIndex:0];

[self downloadWithUrlString:first[@"urlString"]
toPath:first[@"destinationPath"]
process:first[@"process"]
completion:first[@"completion"]
failure:first[@"failure"]];
//从排队对列中移除一个下载任务
[_queue removeObjectAtIndex:0];
}
}
}

添加下载任务:判断是否超过允许的最大的并发任务数,若大于,则进入队列派对,反之则搞一个下载类去下载这个任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-(void)downloadWithUrlString:(NSString *)urlString toPath:(NSString *)destinationPath process:(ProcessHandle)process completion:(CompletionHandle)completion failure:(FailureHandle)failure{

//若同时下载的任务数超过最大同时下载任务数,
//则把下载任务存入对列,在下载完成后,自动进入下载。
if(_taskDict.count>=kFGGDwonloadMaxTaskCount){

NSDictionary *dict=@{@"urlString":urlString,
@"destinationPath":destinationPath,
@"process":process,
@"completion":completion,
@"failure":failure};
[_queue addObject:dict];

return;
}
FGGDownloader *downloader=[FGGDownloader downloader];
@synchronized (self) {
[_taskDict setObject:downloader forKey:urlString];
}
[downloader downloadWithUrlString:urlString
toPath:destinationPath
process:process
completion:completion
failure:failure];
}

取消下载任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 取消下载任务
*
* @param url 下载的链接
*/
-(void)cancelDownloadTask:(NSString *)url{

FGGDownloader *downloader=[_taskDict objectForKey:url];
[downloader cancel];
@synchronized (self) {
[_taskDict removeObjectForKey:url];
}
if(_queue.count>0){

NSDictionary *first=[_queue objectAtIndex:0];

[self downloadWithUrlString:first[@"urlString"]
toPath:first[@"destinationPath"]
process:first[@"process"]
completion:first[@"completion"]
failure:first[@"failure"]];
//从排队对列中移除一个下载任务
[_queue removeObjectAtIndex:0];
}
}

取消所有下载任务:

1
2
3
4
5
6
7
8
-(void)cancelAllTasks{

[_taskDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
FGGDownloader *downloader=obj;
[downloader cancel];
[_taskDict removeObjectForKey:key];
}];
}

彻底移除下载任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 彻底移除下载任务
*
* @param url 下载链接
* @param path 文件路径
*/
-(void)removeForUrl:(NSString *)url file:(NSString *)path{

FGGDownloader *downloader=[_taskDict objectForKey:url];
if(downloader){
[downloader cancel];
}
@synchronized (self) {
[_taskDict removeObjectForKey:url];
}
NSUserDefaults *usd=[NSUserDefaults standardUserDefaults];
NSString *totalLebgthKey=[NSString stringWithFormat:@"%@totalLength",url];
NSString *progressKey=[NSString stringWithFormat:@"%@progress",url];
[usd removeObjectForKey:totalLebgthKey];
[usd removeObjectForKey:progressKey];
[usd synchronize];

NSFileManager *fileManager=[NSFileManager defaultManager];
BOOL fileExist=[fileManager fileExistsAtPath:path];
if(fileExist){

[fileManager removeItemAtPath:path error:nil];
}
}

根据url获取上次下载进度:

1
2
3
4
-(float)lastProgress:(NSString *)url{

return [FGGDownloader lastProgress:url];
}

根据url获取上次下载大小:

1
2
3
4
-(NSString *)filesSize:(NSString *)url{

return [FGGDownloader filesSize:url];
}

最后在销毁的时候,移除通知:

1
2
3
4
-(void)dealloc{

[[NSNotificationCenter defaultCenter] removeObserver:self];
}

最后附上我的GitHub代码地址:FGGDownloader,欢迎pull request和star ~