断点续传功能的实现
来源:互联网 发布:五常大米价格 知乎 编辑:程序博客网 时间:2024/06/06 03:19
断点续传功能的实现
- 断点续传功能的实现
- 下载状态和下载处理Block的定义
- 定义下载任务
- CMDownloadTask类的定义
- changeCurrentStatus类方法实现
- 开始下载
- 暂停下载
- 取消下载
- 通知观察者
- NSURLSessionDownloadDelegate协议方法
- 任务列表
- 结语
项目中需要使用到断点续传的功能,用于群文件共享的下载,本来是打算使用公司原来用C++写的下载,但是实在太麻烦、不方便,几经思考,决定自己动手使用NSURL系列方法来实现,现将代码纪录以供将来重复使用。
下载状态和下载处理Block的定义
首先需要定义下载的状态以及接收到数据时留出的回调Block,包括一些宏定义,如下所示:
typedef NS_ENUM(NSUInteger, DownloadStatus) { DownloadStatusNone, //无状态 DownloadStatusDowloading, //正在下载 DownloadStatusPaused, //暂停下载 DownloadStatusCancelled, //已取消下载 DownloadStatusDownloaded //完成下载};typedef void(^DownloadProgressHandler)(CGFloat, CGFloat);#define DOWNLOAD_FILE_PATH(fileName) [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"ulp/download/%@",(fileName)]]#define DOWNLOAD_FILE_PATH_WITH_EXTENSION(fileName,fileExtension [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"ulp/download/%@.%@",(fileName),(fileExtension)]]#define DownloadFailedNotification @"DownloadFailedNotification"#define DownloadCompleteNotification @"DownloadCompleteNotification"
DownloadProgressHandler主要是留给UI用于更新UIProgressView及其他指示性的文本。
定义下载任务
CMDownloadTask类的定义
类CMDownloadTask表示一个可能处在任何状态的一个下载任务,代码如下所示:
@interface CMDownloadTask : NSObject <NSURLSessionDownloadDelegate> { DownloadStatus theCurrentStatus; NSMutableDictionary* observerDict;}@property(nonatomic,strong) NSURLSessionDownloadTask* downloadTask;@property(nonatomic,strong) NSData* resumeData;@property(nonatomic,copy) NSString* downloadUrl;@property(nonatomic,copy) NSString* downloadFileName;@property(nonatomic,strong) NSDate* previousDownloadDate;-(instancetype)initWithUrl:(NSString*)urlStr downloadFileName:(NSString*)fileName;-(DownloadStatus)getCurrentStatus;-(void)changeCurrentStatus:(DownloadStatus)status;-(void)addObserverWithBlock:(DownloadProgressHandler)downloadHandler WithUrl:(NSString*)urlStr;-(void)removeObserverWithUrl:(NSString*)urlStr;-(void)removeAllObservers;@end
其中的observerDict是用于实现观察者模式,用户可以通过addObserver/removeObserver/removeAllObservers来增加和删除观察者。
changeCurrentStatus类方法实现
changeCurrentStatus则是使用了状态模式,通过状态的切换来执行不同的操作,代码如下所示:
-(void)changeCurrentStatus:(DownloadStatus)status { if(status == theCurrentStatus) return ; switch (status) { case DownloadStatusDowloading: [self startDownload]; break; case DownloadStatusDownloaded: [[CMDownloadTaskList getSharedInstance] removeDownloadTaskByUrl:self.downloadUrl]; [[NSNotificationCenter defaultCenter] postNotificationName:DownloadCompleteNotification object:nil]; break; case DownloadStatusCancelled: [self cancelDownload]; break; case DownloadStatusPaused: [self pauseDownload]; break; default: break; } theCurrentStatus = status; return ;}
开始下载
-(void)startDownload { NSURLSession* downloadSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil]; __weak typeof(self) weakSelf = self; if(self.resumeData == nil) self.downloadTask = [downloadSession downloadTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.downloadUrl]]]; else self.downloadTask = [downloadSession downloadTaskWithResumeData:self.resumeData]; self.previousDownloadDate = [NSDate date]; [self.downloadTask resume]; [[CMDownloadTaskList getSharedInstance] addDownloadTask:self]; return ;}
resumeData用于保存已下载的数据,当resumeData为空的时候,表示没有下载过任何数据,初始化一个下载任务并立即开始下载。如果已经保存有下载的数据,则使用已下载的数据再继续下载,即使用self.resumeData初始化下载任务。
暂停下载
-(void)pauseDownload { __weak typeof(self) weakSelf = self; [self.downloadTask cancelByProducingResumeData:^(NSData *resumeData) { weakSelf.resumeData = resumeData; }]; return ;}
暂停下载的代码非常简单,主要就是将已经下载的部分数据保存到self.resumeData当中,然后取消当前下载。
取消下载
-(void)cancelDownload { [self.downloadTask cancel]; if([[NSFileManager defaultManager] fileExistsAtPath:DOWNLOAD_FILE_PATH(self.downloadFileName)]) [[NSFileManager defaultManager] removeItemAtPath:DOWNLOAD_FILE_PATH(self.downloadFileName) error:nil]; self.downloadTask = nil; return ;}
取消下载之后,如果原来已经下载的部分以文件形式存在,则删除该文件,同时取消下载。
通知观察者
当下载的数据有更新之后,需要通知所有已注册的观察者,故采用方法notifyAllToUpdateWithBytesWritten:TotalBytesToWrite:方法使观察者更新UI,代码如下所示:
-(void)notifyAllToUpdateWithBytesWritten:(CGFloat)bytesWritten TotalBytesToWrite:(CGFloat)totalBytes { NSEnumerator* enumerator = [observerDict keyEnumerator]; id key = nil; while(key = [enumerator nextObject]) { ((DownloadProgressHandler) [observerDict objectForKey:key])(bytesWritten, totalBytes); } return ;}
NSURLSessionDownloadDelegate协议方法
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { if(error) { [self cancelDownload]; [[NSNotificationCenter defaultCenter] postNotificationName:DownloadFailedNotification object:nil]; } return ;}-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { NSDate* currentDate = [NSDate date]; [self notifyAllToUpdateWithBytesWritten:totalBytesWritten TotalBytesToWrite:totalBytesExpectedToWrite]; return ;}-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSError* error = nil; [[NSFileManager defaultManager] moveItemAtPath:location.path toPath:DOWNLOAD_FILE_PATH(self.downloadFileName) error:&error]; if(error) NSLog(@"MoveItemToPath:%@-------------Error:%@",DOWNLOAD_FILE_PATH(self.downloadFileName),error.localizedFailureReason); else NSLog(@"MoveItemToPath:--------------%@",DOWNLOAD_FILE_PATH(self.downloadFileName)); [self changeCurrentStatus:DownloadStatusDownloaded]; return ;}
当有下载数据更新的时候,会进入到上面的代码中,当下载失败的时候,发出通知。写入数据的时候,则通知观察者更新UI,当下载完成之后,将下载完的数据移动到指定的目录当中,并修改相应的状态。
任务列表
为支持多任务下载,开辟一个单例链表类,以url做为标识,保存一个CMDownloadTask,代码如图所求:
@interface CMDownloadTaskList : NSObject@property(nonatomic,strong) NSMutableArray* downloadTaskList;+(CMDownloadTaskList*)getSharedInstance;-(CMDownloadTask*)getCurrentDownloadTaskByUrl:(NSString*)urlStr;-(void)addDownloadTask:(CMDownloadTask*)downloadTask;-(void)removeDownloadTaskByUrl:(NSString*)urlStr;@end
用户则可以通过url来获取、增加、删除相应的下载任务。该类的全部代码如下所示:
static CMDownloadTaskList* instance = nil;@implementation CMDownloadTaskList+(CMDownloadTaskList *)getSharedInstance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if(instance == nil) { instance = [[CMDownloadTaskList alloc] init]; instance.downloadTaskList = [NSMutableArray array]; } }); return instance;}-(void)addDownloadTask:(CMDownloadTask *)downloadTask { for (CMDownloadTask* task in self.downloadTaskList) { if([task.downloadUrl isEqualToString:downloadTask.downloadUrl]) return ; } [self.downloadTaskList addObject:downloadTask]; return ;}-(void)removeDownloadTaskByUrl:(NSString *)urlStr { for (CMDownloadTask* task in self.downloadTaskList) { if([task.downloadUrl isEqualToString:urlStr]) { [task removeAllObservers]; [self.downloadTaskList removeObject:task]; return ; } } return ;}-(CMDownloadTask *)getCurrentDownloadTaskByUrl:(NSString *)urlStr { for (CMDownloadTask* task in self.downloadTaskList) { if([task.downloadUrl isEqualToString:urlStr]) return task; } return nil;}@end
结语
全部代码到这就全部结束了,这几个类并不完善,以后也许会随着项目的要求来完善。
- 断点续传功能的实现
- 实现断点续传功能的下载
- 实现断点续传功能的下载
- Java实现的断点续传功能
- Java实现的断点续传功能
- Java实现的断点续传功能
- wget 的断点续传功能
- android下载的断点续传的功能的实现
- 使用.NET实现断点续传功能
- Android 断点续传下载功能实现
- unity中实现断点续传功能
- Android使用AsyncTask实现可以断点续传的DownloadManager功能
- Android使用AsyncTask实现可以断点续传的DownloadManager功能
- Android使用AsyncTask实现可以断点续传的DownloadManager功能
- Android使用AsyncTask实现可以断点续传的DownloadManager功能
- Android使用AsyncTask实现可以断点续传的DownloadManager功能
- FTP协议简介与断点续传功能的实现
- 多线程断点续传的实现
- ToolBar学习
- Hibernate与Jpa的关系,终于弄懂
- hdu 5547 Sudoku
- STK Tutorial
- xamarin
- 断点续传功能的实现
- matlab--TLD配置
- Core Animation之CATranstion
- 定制替换Android桌面(home screen) Launcher
- python __class__ type理解
- Hibernate之双向一对多关系总结
- 用Android Studio对应用签名打包
- http协议
- Java NIO系列教程(六) Selector