iOS中大文件下载(单线程下载)
主要是需要注意,在客服端发请求给服务器的时候,在请求头里是可以设置服务器返回的数据从哪开始,到哪结束的.
当服务器响应客户端时,是可以拿到服务器返回数据具体类型以及大小的
思路:
在接收到服务器响应时,创建一个空文件,和一个跟空文件相关联的句柄对象
然后,在接收到服务器返回的数据时,利用句柄对象,移动到文件末尾,将数据拼接到文件末尾
在接受完服务器返回的数据后,关闭句柄
#import <Foundation/Foundation.h> @interface ZYFileDownLoad : NSObject //所需要下载文件的远程URL(连接服务器的路径) @property (nonatomic, strong) NSString *urlStr; //文件的存储路径 @property (nonatomic, strong) NSString *goalPath; //是否正在下载 @property (nonatomic, readonly, getter= isDownLoading) BOOL downLoading; //监听下载进度 @property (nonatomic, copy) void (^progressHandler)(double progress); //下载完成后的回调 @property (nonatomic, copy) void (^finishHandler)(); //下载失败后的回调 @property (nonatomic, copy) void (^failureHandler)(NSError *error); //开始下载 - (void)start; //暂停下载 - (void)pause; @end /* 文件一般保存在下面的地址,Documents路径下的文件需要同步,文件大的话消耗性能,tmp路径下随时会被删除 所以一般保存在Library\Caches路径下 获取此路径: NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSString *filepath = [caches stringByAppendingPathComponent:@"文件名.后缀名"]; */ #import "ZYFileDownLoad.h" @interface ZYFileDownLoad() <NSURLConnectionDataDelegate> //当前已下载的数据长度 @property (nonatomic, assign) long long currentLength; //连接对象 @property (nonatomic, strong) NSURLConnection *connection; //总的文件长度 @property (nonatomic, assign) long long totalLength; //写入文件句柄 @property (nonatomic, strong) NSFileHandle *writeHandle; @end @implementation ZYFileDownLoad - (void)start { [self.urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *url = [NSURL URLWithString:self.urlStr]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; //设置请求头信息,这里表示,此次数据范围是currentProgress 到文件最后(也就是只设置开头从什么地方下载) NSString *value = [NSString stringWithFormat:@"bytes=%lld-",self.currentLength]; [request setValue:value forHTTPHeaderField:@"Range"]; self.connection = [NSURLConnection connectionWithRequest:request delegate:self]; _downLoading = YES; } - (void)pause { [self.connection cancel]; self.connection = nil; _downLoading = NO; } #pragma mark ----NSURLConnectionDataDelegate //请求错误(失败)的时候调用 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { if (self.failureHandler) { self.failureHandler(error); } } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { if (self.totalLength) return; //当已经回复过响应了,就无需再次回复(主要用于暂停下载,再回复下载操作) //创建一个空的文件到沙盒中 NSFileManager *manager = [NSFileManager defaultManager]; [manager createFileAtPath:self.goalPath contents:nil attributes:nil]; ////创建写数据的文件句柄 self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:self.goalPath]; //获得完整文件的长度(服务器响应客户端时,会返回具体具体的文件长度) self.totalLength = response.expectedContentLength; } //当接受到服务器的数据就会调用(可能会被调用多次, 每次调用只会传递部分数据) - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { self.currentLength += [data length]; //下载完成,累加长度 //进度 double progress = (double)self.currentLength / self.totalLength; //通过block回调,可以执行block里面的操作 if (self.progressHandler) { self.progressHandler(progress); } //移动文件尾部,往尾部追加数据 [self.writeHandle seekToEndOfFile]; [self.writeHandle writeData:data]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { //下载完毕,请客属性 self.currentLength = 0; self.totalLength = 0; if (self.currentLength == self.totalLength) { //下载完毕,关闭连接 [self.writeHandle closeFile]; self.writeHandle = nil; } if (self.finishHandler) { self.finishHandler(); } } @end
朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。