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
原文地址:https://www.cnblogs.com/ziyi--caolu/p/4676223.html