iOS开发中多线程断点下载大文件
主要思想,就是创建一个与目标文件等大小的空白文件,然后分段往这个空白文件中写入数据。
可以通过发送HEAD请求,获得服务器中文件的具体大小,然后再将这样的长度分割成若干等大的数据块,在发送get请求时,通过设置
请求头信息,可以确定好单个线程中下载文件的起始长度和结束长度。
比如说,目标文件大小事900M,在下载时,开三条线程来下载,size = 300。那么第一条线程就该是0~~299M的任务
第二条线程是300~~599M的任务,第三条则是600M~~最后的大小,当不能整除时,每个size+1,以免造成错误。
在写入文件的适合(这是边下载边写入),通过文件句柄(NSFileHandle)移动到当前下载的文件的末尾,再写入.
当下载完毕,需要关闭当前的文件句柄。
@interface ZYFileDownLoad : NSObject { BOOL _downLoading; } //是否正在下载 @property (nonatomic, readonly, getter=isDownLoading) BOOL downLoading; //目标路径,也就是存储路径 @property (nonatomic, copy) NSString *goalPath; //下载资源的url @property (nonatomic, copy) NSString *urlStr; //下载进度 @property (nonatomic, copy) void (^progressHandler)(double progress); //开始下载 - (void)start; //结束下载 - (void)pause; @end #import "ZYFileDownLoad.h" @implementation ZYFileDownLoad @end
#import "ZYFileDownLoad.h" @interface ZYMultiFileDownLoad : ZYFileDownLoad @end #import "ZYMultiFileDownLoad.h" #import "ZYSingleDownLoad.h" #define ZYMaxDownLoadCount 3 @interface ZYMultiFileDownLoad () @property (nonatomic, strong) NSMutableArray *singleDownLoads; @property (nonatomic, assign) long long totalLength; @end @implementation ZYMultiFileDownLoad - (void)getFileSize { //发送一个HEAD请求,得到服务器响应头中数据的具体信息 Content-Length NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:self.urlStr]]; request.HTTPMethod = @"HEAD"; NSURLResponse *response = nil; //也可发送异步请求获得信息 [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil]; //取得文件大小 self.totalLength = response.expectedContentLength; } - (NSMutableArray *)singleDownLoads { if (_singleDownLoads == nil) { _singleDownLoads = [NSMutableArray array]; [self getFileSize]; long long size = 0; //每条路径的下载量 if (self.totalLength % ZYMaxDownLoadCount == 0) { size = self.totalLength / ZYMaxDownLoadCount; } else{ size = self.totalLength / ZYMaxDownLoadCount + 1; } //创建n个下载器 for (int i = 0; i < ZYMaxDownLoadCount; i++) { ZYSingleDownLoad *singleDownLoad = [[ZYSingleDownLoad alloc] init]; singleDownLoad.urlStr = self.urlStr; singleDownLoad.goalPath = self.goalPath; singleDownLoad.beginLength = i * size; singleDownLoad.endLength = (i + 1) * size - 1; singleDownLoad.progressHandler = ^(double progress){ //在这里可以设置进度操作 }; [_singleDownLoads addObject:singleDownLoad]; // 创建一个跟服务器文件等大小的临时文件 [[NSFileManager defaultManager] createFileAtPath:self.goalPath contents:nil attributes:nil]; // 让self.goalPath文件的长度是self.totalLengt NSFileHandle *writeHandle = [NSFileHandle fileHandleForWritingAtPath:self.goalPath]; [writeHandle truncateFileAtOffset:self.totalLength]; } } return _singleDownLoads; } - (void)start { [self.singleDownLoads makeObjectsPerformSelector:@selector(start)]; _downLoading = YES; } - (void)pause { [self.singleDownLoads makeObjectsPerformSelector:@selector(pause)]; _downLoading = NO; } @end
@interface ZYSingleDownLoad : ZYFileDownLoad @property (nonatomic, assign) long long beginLength; @property (nonatomic, assign) long long endLength; @end #import "ZYSingleDownLoad.h" @interface ZYSingleDownLoad() <NSURLConnectionDataDelegate> @property (nonatomic, assign) long long currentLength; @property (nonatomic, strong) NSURLConnection *connection; @property (nonatomic, strong) NSFileHandle *writeHandle; @end @implementation ZYSingleDownLoad - (NSFileHandle *)writeHandle { if (!_writeHandle) { _writeHandle = [NSFileHandle fileHandleForWritingAtPath:self.goalPath]; } return _writeHandle; } - (void)start { NSURL *url = [NSURL URLWithString:self.urlStr]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; //设置请求体,从哪个数据段开始下载 NSString *values = [NSString stringWithFormat:@"bytes=%lld-%lld",self.beginLength + self.currentLength,self.endLength]; [request setValue:values forHTTPHeaderField:@"Range"]; self.connection = [NSURLConnection connectionWithRequest:request delegate:self]; _downLoading = YES; } - (void)pause { [self.connection cancel]; self.connection = nil; } #pragma mark -------- NSURLConnectionDataDelegate - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 移动到文件的尾部 [self.writeHandle seekToFileOffset:self.beginLength + self.currentLength]; // 从当前移动的位置(文件尾部)开始写入数据 [self.writeHandle writeData:data]; // 累加长度 self.currentLength += data.length; // 打印下载进度 double progress = (double)self.currentLength / (self.endLength - self.beginLength); if (self.progressHandler) { self.progressHandler(progress); } } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { // 清空属性值 self.currentLength = 0; // 关闭连接 [self.writeHandle closeFile]; self.writeHandle = nil; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { } @end
朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。