ios ftp多线程下载 ios多线程下载app_ios 多线程下载队列



一. 断点下载文件

1. 实现功能

点击"开始下载" -> 开始下载文件,进度条实时变化

点击"暂停下载" -> 暂停下载,进度条停止走动

点击"恢复下载" -> 接着上一次下载进度下载

将正在下载或在暂停没有下载完时,将APP关闭,在重新打开时,进度条现在之前的下载进度,点击开始下载,接着上次下载


2. 思路

    1> 要实现断点下载,需要将已下载文件存放到cache缓存中,且把文件总大小存放到cache缓存中,程序每次启动,获取沙盒中已下载文件的大小和文件中大小,拿出这两个值之后就可以设置进度条的进度

    2> 点击开始按钮: 

  • 创建一个NSURLSession会话对象,设置代理
  • 创建一个NSURLSessionDataTask任务发送请求,创建请求时设置请求头部信息,声明文件从哪里开始下载

    3> 实现代理方法

  • 在接收服务器响应方法中获取到当前下载文件的大小,并以字典形式写入到沙盒缓存中,创建输出流用来追加写入数据到沙盒中
  • 在接收数据方法中,输出流对象调用写入方法将接收到的数据写入到沙盒中
  • 在完成数据接收方法中,将关闭输出流对象,并将其指针为nil

3. 实现代码

1> 点击开始,开启任务

// 开始下载
- (IBAction)start {
 
 [self.dataTask resume];
 
}

2> 点击暂停,暂停下载

// 暂停下载
- (IBAction)suspond {
 
 [self.dataTask suspend];
}

3> 点击恢复,恢复下载

// 恢复下载
- (IBAction)recover {
 
 [self.dataTask resume];
 
}

4> 创建session会话,并设置代理

- (NSURLSession *)session
{
 if (_session == nil) {
 
 // 1.创建session会话
        _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
 }
 return _session;
}

5> 创建NSURLSessionDataTask任务

请求头部信息中声明从哪里开始下载

- (NSURLSessionDataTask *)dataTask
{
 if (_dataTask == nil) {
 
 // 下载完成直接返回,不发送网络请求
 NSLog(@"curSize = %zd --- %zd", self.curSize, [self getTotalSize]);
 if (self.curSize == [self getTotalSize] && self.curSize != 0) {
 NSLog(@"下载完成");
 return nil;
 }
 
 // 2.创建dataTask任务
 /*
         表示头500个字节:Range: bytes=0-499
         表示第二个500字节:Range: bytes=500-999
         表示最后500个字节:Range: bytes=-500
         表示500字节以后的范围:Range: bytes=500-
         */
        NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_04.mp4"];
 // 创建请求
 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
 
 NSString *range = [NSString stringWithFormat:@"bytes=%zd-", self.curSize];
 
 [request setValue:range forHTTPHeaderField:@"Range"];
 
        _dataTask = [self.session dataTaskWithRequest:request];
 }
 return _dataTask;
}

6> 代理方法实现

  • 在接收服务器响应方法中获取到当前下载文件的大小,并以字典形式写入到沙盒缓存中,创建输出流用来追加写入数据到沙盒中
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
 
 // 如何处理服务器返回的数据
    completionHandler(NSURLSessionResponseAllow);
 
    self.totalSize = response.expectedContentLength + self.curSize;
 
 
 // 将文件数据的大小保存到沙盒中
 // 取出沙盒中保存数据大小的字典
 NSString *totalSizePath = [NSString stringGetCacheFullPathToWithFileName:KtotalSize];
 
 NSMutableDictionary *dictM = [NSMutableDictionary dictionaryWithContentsOfFile:totalSizePath];
 if (dictM == nil) { // 如果没有初始化字典
        dictM = [NSMutableDictionary dictionary];
 }
 
 // 给字典赋值
    dictM[KfileName] = @(self.totalSize);
 
 // 再将字典回写到沙盒中
 [dictM writeToFile:totalSizePath atomically:YES];
 
 // 拼接沙盒路径
 NSString *filePath = [NSString stringGetCacheFullPathToWithFileName:KfileName];
 
    self.filePath = filePath;
 
 
 // 创建输出流
 NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:filePath append:YES];
    self.stream = stream;
 [stream open];
}
  • 在接收数据方法中,输出流对象调用写入方法将接收到的数据写入到沙盒中
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
 NSLog(@"didReceiveData");
 
 NSLog(@"%zd", data.length);
 
 // 已下载数据的大小
    self.curSize += data.length;
 
 // 下载进度
    self.progressView.progress = 1.0 * self.curSize / self.totalSize;
    self.label.text = [NSString stringWithFormat:@"%f", 1.0 * self.curSize / self.totalSize];
 
 // 数据存放到本地
 [self.stream write:data.bytes maxLength:data.length];
 
}
  • 在完成数据接收方法中,将关闭输出流对象,并将其指针为nil
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
 NSLog(@"didCompleteWithError");
 [self.stream close];
    self.stream = nil;
}

7> 程序关闭在打开现在当前进度

- (void)viewDidLoad {
 [super viewDidLoad];
 
 // 1.获取沙盒中已下载文件的大小
 NSString *filePath = [NSString stringGetCacheFullPathToWithFileName:KfileName];
 NSLog(@"%@", filePath);
 NSFileManager *manager = [NSFileManager defaultManager];
 
 NSDictionary *fileDict = [manager attributesOfItemAtPath:filePath error:nil];
 
    self.curSize = [fileDict[@"NSFileSize"] integerValue];
 NSLog(@"%zd", self.curSize);
 
 // 2.获取沙盒中数据总大小
    self.totalSize = [self getTotalSize];
 
 
 if (self.totalSize == 0) {
 NSLog(@"第一次下载");
 } else {
        self.progressView.progress = 1.0 * self.curSize / self.totalSize;
 }
}

获取沙盒中文件总大小

// 获取沙盒中文件总大小
- (NSInteger)getTotalSize
{
 NSString *totalSizePath = [NSString stringGetCacheFullPathToWithFileName:KtotalSize];
 NSLog(@"%@", totalSizePath);
 NSMutableDictionary *dictM = [NSMutableDictionary dictionaryWithContentsOfFile:totalSizePath];
 NSLog(@"%@", dictM);
 if (dictM == nil) {
 return 0;
 } else {
 NSLog(@"------%zd", [dictM[KfileName] integerValue]);
 return [dictM[KfileName] integerValue];
 
 }
 
}

8> 凡创建的会话都需要在dealloc中释放内存

- (void)dealloc
{
 // 会话释放
 [self.session invalidateAndCancel];
}