zoukankan      html  css  js  c++  java
  • iOS开发网络篇—多线程断点下载

    iOS开发网络篇—多线程断点下载

    说明:本文介绍多线程断点下载。项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件。因为实现过程较为复杂,所以下面贴出完整的代码。

    实现思路:下载开始,创建一个和要下载的文件大小相同的文件(如果要下载的文件为100M,那么就在沙盒中创建一个100M的文件,然后计算每一段的下载量,开启多条线程下载各段的数据,分别写入对应的文件部分)。

    项目中用到的主要类如下:

    完成的实现代码如下:

    主控制器中的代码:

    复制代码
     1 #import "YYViewController.h"
     2 #import "YYFileMultiDownloader.h"
     3 
     4 @interface YYViewController ()
     5 @property (nonatomic, strong) YYFileMultiDownloader *fileMultiDownloader;
     6 @end
     7 
     8 @implementation YYViewController
     9 -  (YYFileMultiDownloader *)fileMultiDownloader
    10 {
    11     if (!_fileMultiDownloader) {
    12         _fileMultiDownloader = [[YYFileMultiDownloader alloc] init];
    13         // 需要下载的文件远程URL
    14         _fileMultiDownloader.url = @"http://192.168.1.200:8080/MJServer/resources/jre.zip";
    15         // 文件保存到什么地方
    16         NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    17         NSString *filepath = [caches stringByAppendingPathComponent:@"jre.zip"];
    18         _fileMultiDownloader.destPath = filepath;
    19     }
    20     return _fileMultiDownloader;
    21 }
    22 
    23 - (void)viewDidLoad
    24 {
    25     [super viewDidLoad];
    26     
    27 }
    28 
    29 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    30 {
    31     [self.fileMultiDownloader start];
    32 }
    33 
    34 @end
    复制代码

    自定义一个基类

    YYFileDownloader.h文件

    复制代码
     1 #import <Foundation/Foundation.h>
     2 
     3 @interface YYFileDownloader : NSObject
     4 {
     5     BOOL _downloading;
     6 }
     7 /**
     8  * 所需要下载文件的远程URL(连接服务器的路径)
     9  */
    10 @property (nonatomic, copy) NSString *url;
    11 /**
    12  * 文件的存储路径(文件下载到什么地方)
    13  */
    14 @property (nonatomic, copy) NSString *destPath;
    15 
    16 /**
    17  * 是否正在下载(有没有在下载, 只有下载器内部才知道)
    18  */
    19 @property (nonatomic, readonly, getter = isDownloading) BOOL downloading;
    20 
    21 /**
    22  * 用来监听下载进度
    23  */
    24 @property (nonatomic, copy) void (^progressHandler)(double progress);
    25 
    26 /**
    27  * 开始(恢复)下载
    28  */
    29 - (void)start;
    30 
    31 /**
    32  * 暂停下载
    33  */
    34 - (void)pause;
    35 @end
    复制代码

    YYFileDownloader.m文件

    1 #import "YYFileDownloader.h"
    2 
    3 @implementation YYFileDownloader
    4 @end

    下载器类继承自YYFileDownloader这个类

    YYFileSingDownloader.h文件

    复制代码
     1 #import "YYFileDownloader.h"
     2 
     3 @interface YYFileSingleDownloader : YYFileDownloader
     4 /**
     5  *  开始的位置
     6  */
     7 @property (nonatomic, assign) long long begin;
     8 /**
     9  *  结束的位置
    10  */
    11 @property (nonatomic, assign) long long end; 
    12 @end
    复制代码

    YYFileSingDownloader.m文件

    复制代码
      1 #import "YYFileSingleDownloader.h"
      2 @interface YYFileSingleDownloader() <NSURLConnectionDataDelegate>
      3 /**
      4  * 连接对象
      5  */
      6 @property (nonatomic, strong) NSURLConnection *conn;
      7 
      8 /**
      9  *  写数据的文件句柄
     10  */
     11 @property (nonatomic, strong) NSFileHandle *writeHandle;
     12 /**
     13  *  当前已下载数据的长度
     14  */
     15 @property (nonatomic, assign) long long currentLength;
     16 @end
     17 
     18 @implementation YYFileSingleDownloader
     19 
     20 - (NSFileHandle *)writeHandle
     21 {
     22     if (!_writeHandle) {
     23         _writeHandle = [NSFileHandle fileHandleForWritingAtPath:self.destPath];
     24     }
     25     return _writeHandle;
     26 }
     27 
     28 /**
     29  * 开始(恢复)下载
     30  */
     31 - (void)start
     32 {
     33     NSURL *url = [NSURL URLWithString:self.url];
     34     // 默认就是GET请求
     35     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
     36     // 设置请求头信息
     37     NSString *value = [NSString stringWithFormat:@"bytes=%lld-%lld", self.begin + self.currentLength, self.end];
     38     [request setValue:value forHTTPHeaderField:@"Range"];
     39     self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
     40     
     41     _downloading = YES;
     42 }
     43 
     44 /**
     45  * 暂停下载
     46  */
     47 - (void)pause
     48 {
     49     [self.conn cancel];
     50     self.conn = nil;
     51     
     52     _downloading = NO;
     53 }
     54 
     55 
     56 #pragma mark - NSURLConnectionDataDelegate 代理方法
     57 /**
     58  *  1. 当接受到服务器的响应(连通了服务器)就会调用
     59  */
     60 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
     61 {
     62     
     63 }
     64 
     65 /**
     66  *  2. 当接受到服务器的数据就会调用(可能会被调用多次, 每次调用只会传递部分数据)
     67  */
     68 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
     69 {
     70     // 移动到文件的尾部
     71     [self.writeHandle seekToFileOffset:self.begin + self.currentLength];
     72     // 从当前移动的位置(文件尾部)开始写入数据
     73     [self.writeHandle writeData:data];
     74     
     75     // 累加长度
     76     self.currentLength += data.length;
     77     
     78     // 打印下载进度
     79     double progress = (double)self.currentLength / (self.end - self.begin);
     80     if (self.progressHandler) {
     81         self.progressHandler(progress);
     82     }
     83 }
     84 
     85 /**
     86  *  3. 当服务器的数据接受完毕后就会调用
     87  */
     88 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
     89 {
     90     // 清空属性值
     91     self.currentLength = 0;
     92     
     93     // 关闭连接(不再输入数据到文件中)
     94     [self.writeHandle closeFile];
     95     self.writeHandle = nil;
     96 }
     97 
     98 /**
     99  *  请求错误(失败)的时候调用(请求超时断网没有网, 一般指客户端错误)
    100  */
    101 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    102 {
    103     
    104 }
    105 
    106 @end
    复制代码

    设计多线程下载器(利用HMFileMultiDownloader能开启多个线程同时下载一个文件)

    一个多线程下载器只下载一个文件

    YYFileMultiDownloader.h文件

    1 #import "YYFileDownloader.h"
    2 
    3 @interface YYFileMultiDownloader : YYFileDownloader
    4 
    5 @end

    YYFileMultiDownloader.m文件

    复制代码
     1 #import "YYFileMultiDownloader.h"
     2 #import "YYFileSingleDownloader.h"
     3 
     4 #define YYMaxDownloadCount 4
     5 
     6 @interface YYFileMultiDownloader()
     7 @property (nonatomic, strong) NSMutableArray *singleDownloaders;
     8 @property (nonatomic, assign) long long totalLength;
     9 @end
    10 
    11 @implementation YYFileMultiDownloader
    12 
    13 - (void)getFilesize
    14 {
    15     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
    16     request.HTTPMethod = @"HEAD";
    17     
    18     NSURLResponse *response = nil;
    19 #warning 这里要用异步请求
    20     [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
    21     self.totalLength = response.expectedContentLength;
    22 }
    23 
    24 - (NSMutableArray *)singleDownloaders
    25 {
    26     if (!_singleDownloaders) {
    27         _singleDownloaders = [NSMutableArray array];
    28         
    29         // 获得文件大小
    30         [self getFilesize];
    31         
    32         // 每条路径的下载量
    33         long long size = 0;
    34         if (self.totalLength % YYMaxDownloadCount == 0) {
    35             size = self.totalLength / YYMaxDownloadCount;
    36         } else {
    37             size = self.totalLength / YYMaxDownloadCount + 1;
    38         }
    39         
    40         // 创建N个下载器
    41         for (int i = 0; i<YYMaxDownloadCount; i++) {
    42             YYFileSingleDownloader *singleDownloader = [[YYFileSingleDownloader alloc] init];
    43             singleDownloader.url = self.url;
    44             singleDownloader.destPath = self.destPath;
    45             singleDownloader.begin = i * size;
    46             singleDownloader.end = singleDownloader.begin + size - 1;
    47             singleDownloader.progressHandler = ^(double progress){
    48                 NSLog(@"%d --- %f", i, progress);
    49             };
    50             [_singleDownloaders addObject:singleDownloader];
    51         }
    52         
    53         // 创建一个跟服务器文件等大小的临时文件
    54         [[NSFileManager defaultManager] createFileAtPath:self.destPath contents:nil attributes:nil];
    55         
    56         // 让self.destPath文件的长度是self.totalLengt
    57         NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:self.destPath];
    58         [handle truncateFileAtOffset:self.totalLength];
    59     }
    60     return _singleDownloaders;
    61 }
    62 
    63 /**
    64  * 开始(恢复)下载
    65  */
    66 - (void)start
    67 {
    68     [self.singleDownloaders makeObjectsPerformSelector:@selector(start)];
    69     
    70     _downloading = YES;
    71 }
    72 
    73 /**
    74  * 暂停下载
    75  */
    76 - (void)pause
    77 {
    78     [self.singleDownloaders makeObjectsPerformSelector:@selector(pause)];
    79     _downloading = NO;
    80 }
    81 
    82 @end
    复制代码

     补充说明:如何获得将要下载的文件的大小?

     
     
  • 相关阅读:
    洛谷 1339 最短路
    洛谷 1330 封锁阳光大学 图论 二分图染色
    洛谷 1262 间谍网络 Tarjan 图论
    洛谷 1373 dp 小a和uim之大逃离 良心题解
    洛谷 1972 莫队
    洛谷 2158 数论 打表 欧拉函数
    洛谷 1414 数论 分解因数 水题
    蒟蒻的省选复习(不如说是noip普及组复习)————连载中
    关于筛法
    关于整数划分的几类问题
  • 原文地址:https://www.cnblogs.com/187n/p/5060909.html
Copyright © 2011-2022 走看看