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

 

posted @ 2015-07-24 22:11  紫忆  阅读(905)  评论(0编辑  收藏  举报