zoukankan      html  css  js  c++  java
  • [iOS 多线程 & 网络

    A.需求
    1. 边下边写入硬盘
    2. 显示下载进度
    3. 暂停/恢复 下载
    4. 解压文件
    5. 多线程下载
     
    B.基本知识
    1.小文件下载
    如果文件比较小,下载方式会比较多
    直接用NSData的+ (id)dataWithContentsOfURL:(NSURL *)url;
    利用NSURLConnection发送一个HTTP请求去下载
    如果是下载图片,还可以利用SDWebImage框架
     
    2.HTTP的Range头信息
    通过设置请求头Range可以指定每次从网路下载数据包的大小
    Range示例
    bytes=0-499 从0到499的头500个字节
    bytes=500-999 从500到999的第二个500字节
    bytes=500- 从500字节以后的所有字节

    bytes=-500 最后500个字节
    bytes=500-599,800-899 同时指定几个范围
    Range小结
    - 用于分隔
    前面的数字表示起始字节数
    后面的数组表示截止字节数,没有表示到末尾
    , 用于分组,可以一次指定多个Range,不过很少用
     
    3.第三方解压框架SSZipArchive
    下载地址:https://github.com/samsoffes/ssziparchive
    注意:需要引入libz.dylib框架 
     1 // Unzipping
     2 NSString *zipPath = @"path_to_your_zip_file";
     3 NSString *destinationPath = @"path_to_the_folder_where_you_want_it_unzipped";
     4 [SSZipArchive unzipFileAtPath:zipPath toDestination:destinationPath];
     5 
     6 // Zipping
     7 NSString *zippedPath = @"path_where_you_want_the_file_created";
     8 NSArray *inputPaths = [NSArray arrayWithObjects:
     9                        [[NSBundle mainBundle] pathForResource:@"photo1" ofType:@"jpg"],
    10                        [[NSBundle mainBundle] pathForResource:@"photo2" ofType:@"jpg"]
    11                        nil];
    12 [SSZipArchive createZipFileAtPath:zippedPath withFilesAtPaths:inputPaths];
     
    Image(42)
     
     
    C.实现
    1.使用NSFileHandle实现边下边写入硬盘
     1 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
     2     NSLog(@"开始接收");
     3    
     4     // 获取存放路径
     5     NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
     6     NSString *filePath = [cachePath stringByAppendingPathComponent:@"0627.zip"];
     7    
     8     // 创建一个空的文件,用来存放接收的数据
     9     NSFileManager *manager = [NSFileManager defaultManager];
    10     [manager createFileAtPath:filePath contents:nil attributes:nil];
    11    
    12     // 把文件句柄用写入方式指向文件
    13     self.handle = [NSFileHandle fileHandleForWritingAtPath:filePath];
    14    
    15 }
    16 
    17 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    18     NSLog(@"正在接收: %d byte", data.length);
    19    
    20     // 移动句柄到上次写入数据的末位置
    21     [self.handle seekToEndOfFile];
    22    
    23     // 写入数据
    24     [self.handle writeData:data];
    25 }
     
    2.显示下载进度条
    使用ProgressView
    从response得到文件大小,再累加每次接收到的数据,计算出完成百分比
    Image(43)
     
     1 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
     2     NSLog(@"开始接收");
     3    
     4     // 获取存放路径
     5     NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
     6     NSString *filePath = [cachePath stringByAppendingPathComponent:@"0627.zip"];
     7    
     8     // 创建一个空的文件,用来存放接收的数据
     9     NSFileManager *manager = [NSFileManager defaultManager];
    10     [manager createFileAtPath:filePath contents:nil attributes:nil];
    11    
    12     // 把文件句柄用写入方式指向文件
    13     self.handle = [NSFileHandle fileHandleForWritingAtPath:filePath];
    14    
    15     // 复位进度条
    16     self.progressView.progress = 0.0;
    17    
    18     // 复位当前下载量
    19     self.currentDataLength = 0;
    20    
    21     // 得到总的文件大小
    22     self.totalDataLength = response.expectedContentLength;
    23    
    24    
    25 }
    26 
    27 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    28     NSLog(@"正在接收: %d byte", data.length);
    29     // 移动句柄到上次写入数据的末位置
    30     [self.handle seekToEndOfFile];
    31    
    32     // 写入数据
    33     [self.handle writeData:data];
    34    
    35     // 下载量累加
    36     self.currentDataLength += data.length;
    37    
    38     // 刷新进度条
    39     CGFloat completedProgress = (double)self.currentDataLength / self.totalDataLength;
    40     NSLog(@"completed percentage: %f%%", completedProgress * 100);
    41     self.progressView.progress = completedProgress;
    42 }
     
    3.暂停/恢复下载
    使用成员属性标记下载状态
    请求得到文件头信息,根据当前下载量,设置请求request头信息,从上次下载完成的地方开始继续下载
    注意:NSURLConnection 取消cancel之后不能恢复,只能再创建
      1 //
      2 //  BigFileDownloadViewController.m
      3 //  BigFileDownloadDemo
      4 //
      5 //  Created by hellovoidworld on 15/1/26.
      6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
      7 //
      8 
      9 #import "BigFileDownloadViewController.h"
     10 
     11 @interface BigFileDownloadViewController ()
     12 
     13 /** 进度条 */
     14 @property (weak, nonatomic) IBOutlet UIProgressView *progressView;
     15 
     16 /** 文件句柄 */
     17 @property(nonatomic, strong) NSFileHandle *handle;
     18 
     19 /** 当前完成的下载量 */
     20 @property(nonatomic, assign) long long currentDataLength;
     21 
     22 /** 总的文件大小 */
     23 @property(nonatomic, assign) long long totalDataLength;
     24 
     25 /** 当前下载连接 */
     26 @property(nonatomic, strong) NSURLConnection *conn;
     27 
     28 /** 下载状态标识 */
     29 @property(nonatomic, assign, getter=isDownloading) BOOL downloading;
     30 
     31 /** 下载/暂停 按钮事件 */
     32 - (IBAction)download:(UIButton *)button;
     33 
     34 @end
     35 
     36 @implementation BigFileDownloadViewController
     37 
     38 - (void)viewDidLoad {
     39     [super viewDidLoad];
     40     // Do any additional setup after loading the view.
     41 }
     42 
     43 
     44 - (IBAction)download:(UIButton *)button {
     45     if (self.isDownloading) { // 如果正在下载中,暂停下载
     46 
     47         self.downloading = NO;
     48         [button setTitle:@"开始" forState:UIControlStateNormal];
     49        
     50         //  取消连接,不能恢复
     51         [self.conn cancel];
     52         self.conn = nil;
     53     } else { // 如果是暂停中,恢复下载
     54        
     55         self.downloading = YES;
     56         [button setTitle:@"暂停" forState:UIControlStateNormal];
     57        
     58         // 从上次下载完成的地方继续下载,初始就是0
     59         NSURL *url = [NSURL URLWithString:@"http://192.168.0.21:8080/MyTestServer/videos/0627.zip"];
     60         NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
     61        
     62         // 设置request头信息,指明要从文件哪里开始下载
     63         NSString *value = [NSString stringWithFormat:@"bytes=%lld-", self.currentDataLength];
     64         [request setValue:value forHTTPHeaderField:@"Range"];
     65        
     66         self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
     67     }
     68 }
     69 
     70 #pragma mark - NSURLConnectionDataDelegate
     71 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
     72     NSLog(@"失败");
     73     NSLog(@"%@", error);
     74 }
     75 
     76 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
     77     NSLog(@"开始接收");
     78    
     79     // 如果是继续下载,就不要再重复创建文件了
     80     if (self.totalDataLength) return;
     81    
     82     // 获取存放路径
     83     NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
     84     NSString *filePath = [cachePath stringByAppendingPathComponent:@"0627.zip"];
     85    
     86     // 创建一个空的文件,用来存放接收的数据
     87     NSFileManager *manager = [NSFileManager defaultManager];
     88     if ([manager fileExistsAtPath:filePath]) {
     89         [manager removeItemAtPath:filePath error:nil];
     90     }
     91    
     92     // 刚创建默认是0字节
     93     [manager createFileAtPath:filePath contents:nil attributes:nil];
     94    
     95    
     96     // 把文件句柄用写入方式指向文件
     97     self.handle = [NSFileHandle fileHandleForWritingAtPath:filePath];
     98    
     99     // 得到总的文件大小
    100     self.totalDataLength = response.expectedContentLength;
    101    
    102    
    103 }
    104 
    105 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    106     NSLog(@"正在接收: %d byte", data.length);
    107 
    108     // 下载量累加
    109     self.currentDataLength += data.length;
    110    
    111     // 刷新进度条
    112     CGFloat completedProgress = (double)self.currentDataLength / self.totalDataLength;
    113     NSLog(@"completed percentage: %f%%", completedProgress * 100);
    114     self.progressView.progress = completedProgress;
    115    
    116     // 移动句柄到上次写入数据的末位置
    117     [self.handle seekToEndOfFile];
    118    
    119     // 写入数据
    120     [self.handle writeData:data];
    121 }
    122 
    123 - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    124     NSLog(@"接收完毕");
    125    
    126     // 复位当前下载量
    127     self.currentDataLength = 0;
    128    
    129     // 复位下载文件大小
    130     self.totalDataLength = 0;
    131    
    132     // 关闭连接
    133     [self.handle closeFile];
    134     self.handle = nil;
    135 }
    136 
    137 
    138 @end
     
    4.文件解压/压缩
    使用ssziparchive-master框架
    注意:需要引入libz.dylib框架
    (1)解压
    使用block存储下载完成后的操作:解压
    1 //     完成下载后block
    2     self.downloader.didFinishHandler = ^{
    3         // 解压文件
    4         [SSZipArchive unzipFileAtPath:filePath toDestination:cachePath];
    5         NSLog(@"解压完成!");
    6     };
     
    #mark: 我使用大小为1.4G,仅包含一个视频的压缩文件进行解压,没有解出来; 解压包含了几张图片的压缩包能正常工作。
     
    (2)压缩
    a.准备实验文件
    Image(44)
     
    b.使用SSZipArchive归档解压
     1     // 获得所有png图片,注意这里是不能获得沙盒中的文件的,只能是项目中的
     2     NSArray *pngs = [[NSBundle mainBundle] pathsForResourcesOfType:@"PNG" inDirectory:nil];
     3     NSLog(@"%@", pngs);
     4    
     5     // 压缩文件存放路径
     6     NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
     7     NSString *filePath = [cachePath stringByAppendingPathComponent:@"pngs.zip"];
     8    
     9     // 压缩文件
    10     [SSZipArchive createZipFileAtPath:filePath withFilesAtPaths:pngs];
    11   
    12     NSLog(@"压缩完成!");
     
     
    5.下载器的封装
    将下载功能封装成一个类,传入请求url和文件存放地址,调用接口开启、暂停线程
     1 //
     2 //  FileDownloader.h
     3 //  BigFileDownloadDemo
     4 //
     5 //  Created by hellovoidworld on 15/1/26.
     6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
     7 //
     8 
     9 #import <Foundation/Foundation.h>
    10 
    11 @interface FileDownloader : NSObject
    12 
    13 /** 请求路径 */
    14 @property(nonatomic, strong) NSURL *url;
    15 
    16 /** 存放路径 */
    17 @property(nonatomic, strong) NSString *filePath;
    18 
    19 /** 下载状态 */
    20 @property(nonatomic, readonly, getter=isDownloading) BOOL downloading;
    21 
    22 /** 更新进度block */
    23 @property(nonatomic, copy) void(^progressHandler)(double progress);
    24 
    25 /** 完成下载后block */
    26 @property(nonatomic, copy) void(^didFinishHandler)();
    27 
    28 - (void) startDownloading;
    29 - (void) pauseDownloading;
    30 
    31 @end
     
      1 //
      2 //  FileDownloader.m
      3 //  BigFileDownloadDemo
      4 //
      5 //  Created by hellovoidworld on 15/1/26.
      6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
      7 //
      8 
      9 #import "FileDownloader.h"
     10 #import <UIKit/UIKit.h>
     11 
     12 @interface FileDownloader() <NSURLConnectionDataDelegate>
     13 
     14 /** 文件句柄 */
     15 @property(nonatomic, strong) NSFileHandle *handle;
     16 
     17 /** 当前完成的下载量 */
     18 @property(nonatomic, assign) long long currentDataLength;
     19 
     20 /** 总的文件大小 */
     21 @property(nonatomic, assign) long long totalDataLength;
     22 
     23 /** 当前下载连接 */
     24 @property(nonatomic, strong) NSURLConnection *conn;
     25 
     26 @end
     27 
     28 @implementation FileDownloader
     29 
     30 /** 开始下载 */
     31 - (void) startDownloading {
     32     _downloading = YES;
     33    
     34     // 从上次下载完成的地方继续下载,初始就是0
     35     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.url];
     36    
     37     // 设置request头信息,指明要从文件哪里开始下载
     38     NSString *value = [NSString stringWithFormat:@"bytes=%lld-", self.currentDataLength];
     39     [request setValue:value forHTTPHeaderField:@"Range"];
     40    
     41     self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
     42 }
     43 
     44 /** 暂停下载 */
     45 - (void) pauseDownloading {
     46     _downloading = NO;
     47    
     48     //  取消连接,不能恢复
     49     [self.conn cancel];
     50     self.conn = nil;
     51 }
     52 
     53 #pragma mark - NSURLConnectionDataDelegate
     54 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
     55     NSLog(@"失败");
     56     NSLog(@"%@", error);
     57 }
     58 
     59 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
     60     NSLog(@"开始接收");
     61    
     62     // 如果是继续下载,就不要再重复创建文件了
     63     if (self.totalDataLength) return;
     64    
     65     // 创建一个空的文件,用来存放接收的数据
     66     NSFileManager *manager = [NSFileManager defaultManager];
     67     if ([manager fileExistsAtPath:self.filePath]) {
     68         [manager removeItemAtPath:self.filePath error:nil];
     69     }
     70    
     71     // 刚创建默认是0字节
     72     [manager createFileAtPath:self.filePath contents:nil attributes:nil];
     73    
     74    
     75     // 把文件句柄用写入方式指向文件
     76     self.handle = [NSFileHandle fileHandleForWritingAtPath:self.filePath];
     77    
     78     // 得到总的文件大小
     79     self.totalDataLength = response.expectedContentLength;
     80    
     81    
     82 }
     83 
     84 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
     85     NSLog(@"正在接收: %d byte", data.length);
     86    
     87     // 下载量累加
     88     self.currentDataLength += data.length;
     89    
     90     // 刷新进度条
     91     CGFloat completedProgress = (double)self.currentDataLength / self.totalDataLength;
     92     NSLog(@"completed percentage: %f%%", completedProgress * 100);
     93     if (self.progressHandler) {
     94         self.progressHandler(completedProgress);
     95     }
     96    
     97     // 移动句柄到上次写入数据的末位置
     98     [self.handle seekToEndOfFile];
     99    
    100     // 写入数据
    101     [self.handle writeData:data];
    102    
    103     NSLog(@"现在的线程:%@", [NSThread currentThread]);
    104 }
    105 
    106 - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    107     NSLog(@"接收完毕");
    108    
    109     // 复位当前下载量
    110     self.currentDataLength = 0;
    111    
    112     // 复位下载文件大小
    113     self.totalDataLength = 0;
    114    
    115     // 关闭连接
    116     [self.handle closeFile];
    117     self.handle = nil;
    118    
    119     if (self.didFinishHandler) {
    120         self.didFinishHandler();
    121     }
    122 }
    123 
    124 @end
     
     1 //
     2 //  BigFileDownloadViewController.m
     3 //  BigFileDownloadDemo
     4 //
     5 //  Created by hellovoidworld on 15/1/26.
     6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
     7 //
     8 
     9 #import "BigFileDownloadViewController.h"
    10 #import "FileDownloader.h"
    11 #import "SSZipArchive.h"
    12 
    13 @interface BigFileDownloadViewController ()
    14 
    15 /** 进度条 */
    16 @property (weak, nonatomic) IBOutlet UIProgressView *progressView;
    17 
    18 /** 下载器 */
    19 @property(nonatomic, strong) FileDownloader *downloader;
    20 
    21 /** 下载/暂停 按钮事件 */
    22 - (IBAction)download:(UIButton *)button;
    23 
    24 @end
    25 
    26 @implementation BigFileDownloadViewController
    27 
    28 - (void)viewDidLoad {
    29     [super viewDidLoad];
    30     // Do any additional setup after loading the view.
    31    
    32     // 设置下载器请求路径
    33     self.downloader = [[FileDownloader alloc] init];
    34     self.downloader.url = [NSURL URLWithString:@"http://192.168.0.21:8080/MyTestServer/videos/0627.zip"];
    35 //    self.downloader.url = [NSURL URLWithString:@"http://192.168.0.21:8080/MyTestServer/images/images.zip"];
    36    
    37     // 设置存放路径
    38     NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    39     NSString *filePath = [cachePath stringByAppendingPathComponent:@"0627.zip"];
    40 //    NSString *filePath = [cachePath stringByAppendingPathComponent:@"images.zip"];
    41     self.downloader.filePath = filePath;
    42    
    43    
    44     // 刷新进度条的block
    45     typeof(self) vc = self;
    46     self.downloader.progressHandler = ^(double progress) {
    47         vc.progressView.progress = progress;
    48     };
    49    
    50 //     完成下载后block
    51     self.downloader.didFinishHandler = ^{
    52         // 解压文件
    53         [SSZipArchive unzipFileAtPath:filePath toDestination:cachePath];
    54         NSLog(@"解压完成!");
    55     };
    56 }
    57 
    58 
    59 - (IBAction)download:(UIButton *)button {
    60     if (self.downloader.isDownloading) { // 如果正在下载中,暂停下载
    61         [button setTitle:@"开始" forState:UIControlStateNormal];
    62         [self.downloader pauseDownloading];
    63     } else { // 如果是暂停中,恢复下载
    64         [button setTitle:@"暂停" forState:UIControlStateNormal];
    65         [self.downloader startDownloading];
    66     }
    67 }
    68 
    69 
    70 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    71    
    72     // 获得所有png图片
    73     NSArray *pngs = [[NSBundle mainBundle] pathsForResourcesOfType:@"PNG" inDirectory:nil];
    74     NSLog(@"%@", pngs);
    75    
    76     // 压缩文件存放路径
    77     NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    78     NSString *filePath = [cachePath stringByAppendingPathComponent:@"pngs.zip"];
    79    
    80     // 压缩文件
    81     [SSZipArchive createZipFileAtPath:filePath withFilesAtPaths:pngs];
    82    
    83     NSLog(@"压缩完成!");
    84 }
    85 
    86 @end
     
     
    6.多线程下载
    思路:由于调用NSURLConnection的代理方法进行服务器请求和接收,发送的是异步请求,所以可以认为利用几个NSURLConnection同时发送请求,下载一个文件的不同部分,就能实现多线程下载。
     
    • 创建多线下载器类和单线下载器
    • 多线下载器对象创建n个单线下载器来进行下载
    • 创建一个跟实际文件大小一样的临时文件,用来辅助定位单线下载器的下载存储位置
    • n个单线下载器负责一个文件的不同部分的下载
    • 使用请求头设置Range来实现文件下载部分的不同
     
    Image(45)
     
    (1)基本下载器
    用来声明了一些公用属性和方法
     1 //
     2 //  FileDownloader.h
     3 //  BigFileDownloadDemo
     4 //
     5 //  Created by hellovoidworld on 15/1/26.
     6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
     7 //
     8 
     9 #import <Foundation/Foundation.h>
    10 
    11 @interface FileDownloader : NSObject
    12 {
    13     @protected
    14     BOOL _downloading;
    15 }
    16 
    17 /** 请求路径 */
    18 @property(nonatomic, strong) NSString *url;
    19 
    20 /** 存放路径 */
    21 @property(nonatomic, strong) NSString *filePath;
    22 
    23 /** 下载状态 */
    24 @property(nonatomic, readonly, getter=isDownloading) BOOL downloading;
    25 
    26 /** 更新进度block */
    27 @property(nonatomic, copy) void(^progressHandler)(double progress);
    28 
    29 /** 完成下载后block */
    30 @property(nonatomic, copy) void(^didFinishHandler)();
    31 
    32 - (void) startDownloading;
    33 - (void) pauseDownloading;
    34 
    35 @end
     
    (2)单线下载器
     1 //
     2 //  SingleDownloader.h
     3 //  MultiDownloaderDemo
     4 //
     5 //  Created by hellovoidworld on 15/1/27.
     6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
     7 //
     8 
     9 #import "FileDownloader.h"
    10 
    11 @interface SingleDownloader : FileDownloader
    12 
    13 /** 开始下载的位置(字节) */
    14 @property(nonatomic, assign) long long begin;
    15 
    16 /** 结束下载的位置(字节) */
    17 @property(nonatomic, assign) long long end;
    18 
    19 @end
     
      1 //
      2 //  SingleDownloader.m
      3 //  MultiDownloaderDemo
      4 //
      5 //  Created by hellovoidworld on 15/1/27.
      6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
      7 //
      8 
      9 #import "SingleDownloader.h"
     10 
     11 @interface SingleDownloader() <NSURLConnectionDataDelegate>
     12 
     13 /** 文件句柄 */
     14 @property(nonatomic, strong) NSFileHandle *handle;
     15 
     16 /** 当前下载连接 */
     17 @property(nonatomic, strong) NSURLConnection *conn;
     18 
     19 /** 当前下载量 */
     20 @property(nonatomic, assign) long long currentLength;
     21 
     22 @end
     23 
     24 @implementation SingleDownloader
     25 
     26 /** 初始化文件句柄 */
     27 - (NSFileHandle *)handle {
     28     if (nil == _handle) {
     29         _handle = [NSFileHandle fileHandleForWritingAtPath:self.filePath];
     30     }
     31     return _handle;
     32 }
     33 
     34 /** 开始下载 */
     35 - (void) startDownloading {
     36     // 从上次下载完成的地方继续下载,初始就是0
     37     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
     38    
     39     // 设置request头信息,指明要从文件哪里开始下载
     40     NSString *value = [NSString stringWithFormat:@"bytes=%lld-%lld", self.begin, self.end];
     41     [request setValue:value forHTTPHeaderField:@"Range"];
     42    
     43     self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
     44    
     45     _downloading = YES;
     46 }
     47 
     48 /** 暂停下载 */
     49 - (void) pauseDownloading {
     50     //  取消连接,不能恢复
     51     [self.conn cancel];
     52     self.conn = nil;
     53    
     54     _downloading = NO;
     55 }
     56 
     57 #pragma mark - NSURLConnectionDataDelegate
     58 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
     59     NSLog(@"失败");
     60     NSLog(@"%@", error);
     61 }
     62 
     63 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
     64     NSLog(@"开始接收");
     65 }
     66 
     67 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
     68     // 移动句柄到上次写入数据的末位置
     69     [self.handle seekToFileOffset:self.begin + self.currentLength];
     70    
     71     // 写入数据
     72     [self.handle writeData:data];
     73    
     74     // 已经下载的量
     75     self.currentLength += data.length;
     76  
     77     if (self.progressHandler) {
     78         double progress = (double)self.currentLength / (self.end - self.begin + 1);
     79         self.progressHandler(progress);
     80     }
     81 }
     82 
     83 - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
     84     NSLog(@"接收完毕");
     85    
     86     // 清空下载量
     87     self.currentLength = 0;
     88    
     89    
     90     // 关闭连接
     91     [self.handle closeFile];
     92     self.handle = nil;
     93    
     94     if (self.didFinishHandler) {
     95         self.didFinishHandler();
     96     }
     97 }
     98 
     99 
    100 @end
    (3)总下载器
      1 //
      2 //  MultiDownloader.h
      3 //  MultiDownloaderDemo
      4 //
      5 //  Created by hellovoidworld on 15/1/27.
      6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
      7 //
      8 
      9 #import "FileDownloader.h"
     10 
     11 @interface MultiDownloader : FileDownloader
     12 
     13 @end
     14  
     15 //
     16 //  MultiDownloader.m
     17 //  MultiDownloaderDemo
     18 //
     19 //  Created by hellovoidworld on 15/1/27.
     20 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
     21 //
     22 
     23 #import "MultiDownloader.h"
     24 #import "SingleDownloader.h"
     25 
     26 #define MaxMultilineCount 4
     27 
     28 @interface MultiDownloader()
     29 
     30 @property(nonatomic, strong) NSMutableArray *singleDownloaders;
     31 
     32 @end
     33 
     34 @implementation MultiDownloader
     35 
     36 /** 初始化单线下载器 */
     37 - (NSMutableArray *)singleDownloaders {
     38     if (!_singleDownloaders) {
     39         _singleDownloaders = [NSMutableArray array];
     40        
     41         long long fileSize = [self getFileSize];
     42         long long singleFileSize = 0; // 每条子线下载量
     43         if (fileSize % MaxMultilineCount == 0) {
     44             singleFileSize = fileSize / MaxMultilineCount;
     45         } else {
     46             singleFileSize = fileSize / MaxMultilineCount + 1;
     47         }
     48        
     49         for (int i=0; i<MaxMultilineCount; i++) {
     50             SingleDownloader *downloader = [[SingleDownloader alloc] init];
     51             downloader.url = self.url;
     52             downloader.filePath = self.filePath;
     53             downloader.begin = i * singleFileSize;
     54             downloader.end = downloader.begin + singleFileSize - 1;
     55             downloader.progressHandler = ^(double progress){
     56                 NSLog(@"%d号单线下载器正在下载,下载进度:%f", i, progress);
     57             };
     58            
     59             [self.singleDownloaders addObject:downloader];
     60         }
     61        
     62         // 创建临时文件,文件大小要跟实际大小一致
     63         // 1.创建一个0字节文件
     64         [[NSFileManager defaultManager] createFileAtPath:self.filePath contents:nil attributes:nil];
     65        
     66         // 2.指定文件大小
     67         NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.filePath];
     68         [fileHandle truncateFileAtOffset:fileSize];
     69     }
     70    
     71     return _singleDownloaders;
     72 }
     73 
     74 /** 获得文件大小 */
     75 - (long long) getFileSize {
     76     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
     77     request.HTTPMethod = @"HEAD";// 请求得到头响应
     78    
     79     NSURLResponse *response = nil;
     80     [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
     81     return response.expectedContentLength;
     82 }
     83 
     84 /** 开始下载 */
     85 - (void)startDownloading {
     86     [self.singleDownloaders makeObjectsPerformSelector:@selector(startDownloading)];
     87     _downloading = YES;
     88     NSLog(@"多线程下载开始");
     89 }
     90 
     91 /** 暂停下载 */
     92 - (void)pauseDownloading {
     93     [self.singleDownloaders makeObjectsPerformSelector:@selector(pauseDownloading)];
     94     _downloading = NO;
     95     NSLog(@"多线程下载暂停!");
     96 }
     97 
     98 @end
     99  
    1004)控制器调用
    101 - (IBAction)startDownloading {
    102     MultiDownloader *downloader = [[MultiDownloader alloc] init];
    103     downloader.url = @"http://192.168.0.21:8080/MyTestServer/videos/0627.zip";
    104    
    105     // 设置存放路径
    106     NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    107     downloader.filePath = [cachePath stringByAppendingPathComponent:@"0627.zip"];
    108    
    109     // 开始下载
    110     [downloader startDownloading];
    111 }
     
     
     
     
  • 相关阅读:
    腾讯之困,QQ与微信各有各的烦恼
    Attribute(一)——提前定义特性
    假设有来生
    codeforces248(div1) B Nanami&#39;s Digital Board
    驳斥苹果“诊断后门论”,声援扎德尔斯基
    python批量下载色影无忌和蜂鸟的图片 爬虫小应用
    建筑建模学习笔记2——3DMax房屋框架建模
    【大话QT之十六】使用ctkPluginFramework插件系统构建项目实战
    javaObject类
    java基本类型和String之间的转换
  • 原文地址:https://www.cnblogs.com/hellovoidworld/p/4252556.html
Copyright © 2011-2022 走看看