(1)使用 NSURLConnection 直接方式
(2)使用 NSURLConnection 代理方式
(3)使用 NSURLSession 直接方式
(4)使用 NSURLSession 代理方式
(5)使用 AFNetworking 方式
(1)使用 AFNetworking 中的 AFNetworkReachabilityManager 来检查网络情况:
AFNetworkReachabilityStatusReachableViaWiFi:Wi-Fi 网络下
AFNetworkReachabilityStatusReachableViaWWAN:2G/3G/4G 蜂窝移动网络下
AFNetworkReachabilityStatusNotReachable:未连接网络
(2)使用 AFNetworking 中的 AFNetworkActivityIndicatorManager 来启动网络活动指示器:
#import "AFNetworkActivityIndicatorManager.h" //启动网络活动指示器;会根据网络交互情况,实时显示或隐藏网络活动指示器;他通过「通知与消息机制」来实现 [UIApplication sharedApplication].networkActivityIndicatorVisible 的控制 [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
效果如下:
ViewController.h
#import @interface ViewController : UITableViewController @property (copy, nonatomic) NSArray *arrSampleName; - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName; @end
ViewController.m
#import "ViewController.h" #import "NSURLConnectionViewController.h" #import "NSURLConnectionDelegateViewController.h" #import "NSURLSessionViewController.h" #import "NSURLSessionDelegateViewController.h" #import "AFNetworkingViewController.h" @interface ViewController () - (void)layoutUI; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [self layoutUI]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName { if (self = [super initWithStyle:UITableViewStyleGrouped]) { self.navigationItem.title = @"多种方式实现文件下载功能"; self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:nil action:nil]; _arrSampleName = arrSampleName; } return self; } - (void)layoutUI { } #pragma mark - UITableViewController相关方法重写 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 0.1; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [_arrSampleName count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; } cell.textLabel.text = _arrSampleName[indexPath.row]; return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { switch (indexPath.row) { case 0: { NSURLConnectionViewController *connectionVC = [NSURLConnectionViewController new]; [self.navigationController pushViewController:connectionVC animated:YES]; break; } case 1: { NSURLConnectionDelegateViewController *connectionDelegateVC = [NSURLConnectionDelegateViewController new]; [self.navigationController pushViewController:connectionDelegateVC animated:YES]; break; } case 2: { NSURLSessionViewController *sessionVC = [NSURLSessionViewController new]; [self.navigationController pushViewController:sessionVC animated:YES]; break; } case 3: { NSURLSessionDelegateViewController *sessionDelegateVC = [NSURLSessionDelegateViewController new]; [self.navigationController pushViewController:sessionDelegateVC animated:YES]; break; } case 4: { AFNetworkingViewController *networkingVC = [AFNetworkingViewController new]; [self.navigationController pushViewController:networkingVC animated:YES]; break; } default: break; } } @end
PrefixHeader.pch
#define kFileURLStr @"http://files.cnblogs.com/files/huangjianwu/metro_demo使用Highcharts实现图表展示.zip" #define kTitleOfNSURLConnection @"使用 NSURLConnection 直接方式" #define kTitleOfNSURLConnectionDelegate @"使用 NSURLConnection 代理方式" #define kTitleOfNSURLSession @"使用 NSURLSession 直接方式" #define kTitleOfNSURLSessionDelegate @"使用 NSURLSession 代理方式" #define kTitleOfAFNetworking @"使用 AFNetworking 方式" #define kApplication [UIApplication sharedApplication]
UIButton+BeautifulButton.h
#import @interface UIButton (BeautifulButton) /** * 根据按钮文字颜色,返回对应文字颜色的圆角按钮 * * @param tintColor 按钮文字颜色;nil 的话就为深灰色 */ - (void)beautifulButton:(UIColor *)tintColor; @end
UIButton+BeautifulButton.m
#import "UIButton+BeautifulButton.h" @implementation UIButton (BeautifulButton) - (void)beautifulButton:(UIColor *)tintColor { self.tintColor = tintColor ?: [UIColor darkGrayColor]; self.layer.masksToBounds = YES; self.layer.cornerRadius = 10.0; self.layer.borderColor = [UIColor grayColor].CGColor; self.layer.borderWidth = 1.0; } @end
NSURLConnectionViewController.h
#import @interface NSURLConnectionViewController : UIViewController @property (strong, nonatomic) IBOutlet UILabel *lblFileName; @property (strong, nonatomic) IBOutlet UILabel *lblMessage; @property (strong, nonatomic) IBOutlet UIButton *btnDownloadFile; @end
NSURLConnectionViewController.m
#import "NSURLConnectionViewController.h" #import "UIButton+BeautifulButton.h" @interface NSURLConnectionViewController () - (void)layoutUI; - (void)saveDataToDisk:(NSData *)data; @end @implementation NSURLConnectionViewController - (void)viewDidLoad { [super viewDidLoad]; [self layoutUI]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)layoutUI { self.navigationItem.title = kTitleOfNSURLConnection; self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.000]; [_btnDownloadFile beautifulButton:nil]; } - (void)saveDataToDisk:(NSData *)data { //数据接收完保存文件;注意苹果官方要求:下载数据只能保存在缓存目录(/Library/Caches) NSString *savePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; savePath = [savePath stringByAppendingPathComponent:_lblFileName.text]; [data writeToFile:savePath atomically:YES]; //writeToFile: 方法:如果 savePath 文件存在,他会执行覆盖 } - (IBAction)downloadFile:(id)sender { _lblMessage.text = @"下载中..."; NSString *fileURLStr = kFileURLStr; //编码操作;对应的解码操作是用 stringByRemovingPercentEncoding 方法 fileURLStr = [fileURLStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *fileURL = [NSURL URLWithString:fileURLStr]; //创建请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:fileURL]; //创建连接;Apple 提供的处理一般请求的两种方法,他们不需要进行一系列的 NSURLConnectionDataDelegate 委托协议方法操作,简洁直观 //方法一:发送一个同步请求;不建议使用,因为当前线程是主线程的话,会造成线程阻塞,一般比较少用 // NSURLResponse *response; // NSError *connectionError; // NSData *data = [NSURLConnection sendSynchronousRequest:request // returningResponse:&response // error:&connectionError]; // if (!connectionError) { // [self saveDataToDisk:data]; // NSLog(@"保存成功"); // // _lblMessage.text = @"下载完成"; // } else { // NSLog(@"下载失败,错误信息:%@", connectionError.localizedDescription); // // _lblMessage.text = @"下载失败"; // } //方法二:发送一个异步请求 [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { if (!connectionError) { [self saveDataToDisk:data]; NSLog(@"保存成功"); _lblMessage.text = @"下载完成"; } else { NSLog(@"下载失败,错误信息:%@", connectionError.localizedDescription); _lblMessage.text = @"下载失败"; } }]; } @end
NSURLConnectionViewController.xib
[?xml version="1.0" encoding="UTF-8" standalone="no"?] [document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES"] [dependencies] [plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/] [/dependencies] [objects] [placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="NSURLConnectionViewController"] [connections] [outlet property="btnDownloadFile" destination="mwt-p9-tRE" id="ZVc-6S-ES3"/] [outlet property="lblFileName" destination="dlB-Qn-eOO" id="NdS-9n-7KX"/] [outlet property="lblMessage" destination="qlQ-nM-BXU" id="tRe-SR-AQE"/] [outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/] [/connections] [/placeholder] [placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/] [view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT"] [rect key="frame" x="0.0" y="0.0" width="600" height="600"/] [autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/] [subviews] [label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="metro_demo使用Highcharts实现图表展示.zip" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dlB-Qn-eOO"] [rect key="frame" x="145" y="104" width="309.5" height="18"/] [fontDescription key="fontDescription" type="boldSystem" pointSize="15"/] [color key="textColor" cocoaTouchSystemColor="darkTextColor"/] [nil key="highlightedColor"/] [/label] [button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mwt-p9-tRE"] [rect key="frame" x="250" y="520" width="100" height="40"/] [constraints] [constraint firstAttribute="width" constant="100" id="I5D-tA-ffH"/] [constraint firstAttribute="height" constant="40" id="Y8C-D4-IVr"/] [/constraints] [state key="normal" title="下载"] [color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/] [/state] [connections] [action selector="downloadFile:" destination="-1" eventType="touchUpInside" id="MK4-Yk-IOk"/] [/connections] [/button] [label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qlQ-nM-BXU"] [rect key="frame" x="145" y="140" width="37.5" height="18"/] [fontDescription key="fontDescription" type="system" pointSize="15"/] [color key="textColor" red="0.0" green="0.50196081399917603" blue="1" alpha="1" colorSpace="calibratedRGB"/] [nil key="highlightedColor"/] [userDefinedRuntimeAttributes] [userDefinedRuntimeAttribute type="string" keyPath="text" value=""/] [/userDefinedRuntimeAttributes] [/label] [/subviews] [color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/] [constraints] [constraint firstAttribute="centerX" secondItem="dlB-Qn-eOO" secondAttribute="centerX" id="gNK-NO-rth"/] [constraint firstItem="dlB-Qn-eOO" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="104" id="hwU-O2-Fed"/] [constraint firstAttribute="centerX" secondItem="mwt-p9-tRE" secondAttribute="centerX" id="teN-3t-8Gc"/] [constraint firstItem="qlQ-nM-BXU" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="140" id="w3g-ej-P18"/] [constraint firstItem="dlB-Qn-eOO" firstAttribute="leading" secondItem="qlQ-nM-BXU" secondAttribute="leading" constant="0.5" id="wMU-pU-z9f"/] [constraint firstAttribute="bottom" secondItem="mwt-p9-tRE" secondAttribute="bottom" constant="40" id="yBq-He-Jv8"/] [/constraints] [/view] [/objects] [/document]
NSURLConnectionDelegateViewController.h
#import @interface NSURLConnectionDelegateViewController : UIViewController @property (strong, nonatomic) NSMutableData *mDataReceive; @property (assign, nonatomic) NSUInteger totalDataLength; @property (strong, nonatomic) IBOutlet UILabel *lblFileName; @property (strong, nonatomic) IBOutlet UIProgressView *progVDownloadFile; @property (strong, nonatomic) IBOutlet UILabel *lblMessage; @property (strong, nonatomic) IBOutlet UIButton *btnDownloadFile; @end
NSURLConnectionDelegateViewController.m
#import "NSURLConnectionDelegateViewController.h" #import "UIButton+BeautifulButton.h" @interface NSURLConnectionDelegateViewController () - (void)layoutUI; - (BOOL)isExistCacheInMemory:(NSURLRequest *)request; - (void)updateProgress; @end @implementation NSURLConnectionDelegateViewController - (void)viewDidLoad { [super viewDidLoad]; [self layoutUI]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)layoutUI { self.navigationItem.title = kTitleOfNSURLConnectionDelegate; self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.000]; [_btnDownloadFile beautifulButton:nil]; } - (BOOL)isExistCacheInMemory:(NSURLRequest *)request { BOOL isExistCache = NO; NSURLCache *cache = [NSURLCache sharedURLCache]; [cache setMemoryCapacity:1024 * 1024]; //1M NSCachedURLResponse *response = [cache cachedResponseForRequest:request]; if (response != nil) { NSLog(@"内存中存在对应请求的响应缓存"); isExistCache = YES; } return isExistCache; } - (void)updateProgress { NSUInteger receiveDataLength = _mDataReceive.length; if (receiveDataLength == _totalDataLength) { _lblMessage.text = @"下载完成"; kApplication.networkActivityIndicatorVisible = NO; } else { _lblMessage.text = @"下载中..."; kApplication.networkActivityIndicatorVisible = YES; _progVDownloadFile.progress = (float)receiveDataLength / _totalDataLength; } } - (IBAction)downloadFile:(id)sender { /* 此例子更多的是希望大家了解代理方法接收响应数据的过程,实际开发中也不可能使用这种方法进行文件下载。这种下载有个致命的问题:无法进行大文件下载。因为代理方法在接收数据时虽然表面看起来是每次读取一部分响应数据,事实上它只有一次请求并且也只接收了一次服务器响应,只是当响应数据较大时系统会重复调用数据接收方法,每次将已读取的数据拿出一部分交给数据接收方法而已。在这个过程中其实早已经将响应数据全部拿到,只是分批交给开发者而已。这样一来对于几个G的文件如果进行下载,那么不用说是真机下载了,就算是模拟器恐怕也是不现实的。 实际开发文件下载的时候不管是通过代理方法还是静态方法执行请求和响应,我们都会分批请求数据,而不是一次性请求数据。假设一个文件有1G,那么只要每次请求1M的数据,请求1024次也就下载完了。那么如何让服务器每次只返回1M的数据呢? 在网络开发中可以在请求的头文件中设置一个Range信息,它代表请求数据的大小。通过这个字段配合服务器端可以精确的控制每次服务器响应的数据范围。例如指定bytes=0-1023,然后在服务器端解析Range信息,返回该文件的0到1023之间的数据的数据即可(共1024Byte)。这样,只要在每次发送请求控制这个头文件信息就可以做到分批请求。 当然,为了让整个数据保持完整,每次请求的数据都需要逐步追加直到整个文件请求完成。但是如何知道整个文件的大小?其实在此例子通过头文件信息获取整个文件大小,他请求整个数据,这样做对分段下载就没有任何意义了。所幸在WEB开发中我们还有另一种请求方法“HEAD”,通过这种请求服务器只会响应头信息,其他数据不会返回给客户端,这样一来整个数据的大小也就可以得到了。 */ NSString *fileURLStr = kFileURLStr; //编码操作;对应的解码操作是用 stringByRemovingPercentEncoding 方法 fileURLStr = [fileURLStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *fileURL = [NSURL URLWithString:fileURLStr]; /*创建请求 cachePolicy:缓存策略 1、NSURLRequestUseProtocolCachePolicy 协议缓存,根据 response 中的 Cache-Control 字段判断缓存是否有效,如果缓存有效则使用缓存数据否则重新从服务器请求 2、NSURLRequestReloadIgnoringLocalCacheData 不使用缓存,直接请求新数据 3、NSURLRequestReloadIgnoringCacheData 等同于 NSURLRequestReloadIgnoringLocalCacheData 4、NSURLRequestReturnCacheDataElseLoad 直接使用缓存数据不管是否有效,没有缓存则重新请求 5、NSURLRequestReturnCacheDataDontLoad 直接使用缓存数据不管是否有效,没有缓存数据则失败 timeoutInterval:超时时间设置(默认60s) */ NSURLRequest *request = [[NSURLRequest alloc] initWithURL:fileURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; if ([self isExistCacheInMemory:request]) { request = [[NSURLRequest alloc] initWithURL:fileURL cachePolicy:NSURLRequestReturnCacheDataDontLoad timeoutInterval:60.0]; } //创建连接,异步操作 NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; [connection start]; //启动连接 } #pragma mark - NSURLConnectionDataDelegate - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response { NSLog(@"即将发送请求"); return request; } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { NSLog(@"已经接收到响应"); _mDataReceive = [NSMutableData new]; _progVDownloadFile.progress = 0.0; //通过响应头中的 Content-Length 获取到整个响应的总长度 /* { "Accept-Ranges" = bytes; "Cache-Control" = "max-age=7776000"; "Content-Length" = 592441; "Content-Type" = "application/x-zip-compressed"; Date = "Wed, 02 Sep 2015 13:17:01 GMT"; Etag = "/"d8f617371f9cd01:0/""; "Last-Modified" = "Mon, 01 Jun 2015 03:58:27 GMT"; Server = "Microsoft-IIS/7.5"; "X-Powered-By" = "ASP.NET"; } */ NSDictionary *dicHeaderField = [(NSHTTPURLResponse *)response allHeaderFields]; _totalDataLength = [[dicHeaderField objectForKey:@"Content-Length"] integerValue]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { NSLog(@"已经接收到响应数据,数据长度为%lu字节...", (unsigned long)[data length]); [_mDataReceive appendData:data]; //连续接收数据 [self updateProgress]; //连续更新进度条 } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"已经接收完所有响应数据"); NSString *savePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; savePath = [savePath stringByAppendingPathComponent:_lblFileName.text]; [_mDataReceive writeToFile:savePath atomically:YES]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { //如果连接超时或者连接地址错误可能就会报错 NSLog(@"连接错误,错误信息:%@", error.localizedDescription); _lblMessage.text = @"连接错误"; } @end
NSURLConnectionDelegateViewController.xib
[?xml version="1.0" encoding="UTF-8" standalone="no"?] [document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES"] [dependencies] [deployment identifier="iOS"/] [plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/] [/dependencies] [objects] [placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="NSURLConnectionDelegateViewController"] [connections] [outlet property="btnDownloadFile" destination="mwt-p9-tRE" id="ZVc-6S-ES3"/] [outlet property="lblFileName" destination="dlB-Qn-eOO" id="vJk-jh-Y2c"/] [outlet property="lblMessage" destination="qlQ-nM-BXU" id="tRe-SR-AQE"/] [outlet property="progVDownloadFile" destination="Me3-m2-iC4" id="PtK-m7-j5N"/] [outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/] [/connections] [/placeholder] [placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/] [view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT"] [rect key="frame" x="0.0" y="0.0" width="600" height="600"/] [autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/] [subviews] [label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="metro_demo使用Highcharts实现图表展示.zip" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dlB-Qn-eOO"] [rect key="frame" x="145" y="104" width="309.5" height="18"/] [fontDescription key="fontDescription" type="boldSystem" pointSize="15"/] [color key="textColor" cocoaTouchSystemColor="darkTextColor"/] [nil key="highlightedColor"/] [/label] [button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mwt-p9-tRE"] [rect key="frame" x="250" y="520" width="100" height="40"/] [constraints] [constraint firstAttribute="width" constant="100" id="I5D-tA-ffH"/] [constraint firstAttribute="height" constant="40" id="Y8C-D4-IVr"/] [/constraints] [state key="normal" title="下载"] [color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/] [/state] [connections] [action selector="downloadFile:" destination="-1" eventType="touchUpInside" id="iGc-6N-bsZ"/] [/connections] [/button] [progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Me3-m2-iC4"] [rect key="frame" x="145" y="160" width="310" height="2"/] [constraints] [constraint firstAttribute="height" constant="2" id="I50-Zx-DwT"/] [constraint firstAttribute="width" constant="310" id="wdS-eD-Tkc"/] [/constraints] [/progressView] [label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qlQ-nM-BXU"] [rect key="frame" x="145" y="180" width="37.5" height="18"/] [fontDescription key="fontDescription" type="system" pointSize="15"/] [color key="textColor" red="0.0" green="0.50196081399917603" blue="1" alpha="1" colorSpace="calibratedRGB"/] [nil key="highlightedColor"/] [userDefinedRuntimeAttributes] [userDefinedRuntimeAttribute type="string" keyPath="text" value=""/] [/userDefinedRuntimeAttributes] [/label] [/subviews] [color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/] [constraints] [constraint firstAttribute="centerX" secondItem="Me3-m2-iC4" secondAttribute="centerX" id="Ya8-bM-TaA"/] [constraint firstItem="Me3-m2-iC4" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="160" id="bOY-B5-is2"/] [constraint firstAttribute="centerX" secondItem="dlB-Qn-eOO" secondAttribute="centerX" id="gNK-NO-rth"/] [constraint firstItem="dlB-Qn-eOO" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="104" id="hwU-O2-Fed"/] [constraint firstItem="qlQ-nM-BXU" firstAttribute="leading" secondItem="Me3-m2-iC4" secondAttribute="leading" id="lus-oi-9SA"/] [constraint firstAttribute="centerX" secondItem="mwt-p9-tRE" secondAttribute="centerX" id="teN-3t-8Gc"/] [constraint firstItem="qlQ-nM-BXU" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="180" id="w3g-ej-P18"/] [constraint firstAttribute="bottom" secondItem="mwt-p9-tRE" secondAttribute="bottom" constant="40" id="yBq-He-Jv8"/] [/constraints] [/view] [/objects] [/document]
NSURLSessionViewController.h
#import @interface NSURLSessionViewController : UIViewController @property (strong, nonatomic) IBOutlet UILabel *lblFileName; @property (strong, nonatomic) IBOutlet UILabel *lblMessage; @property (strong, nonatomic) IBOutlet UIButton *btnDownloadFile; @end
NSURLSessionViewController.m
#import "NSURLSessionViewController.h" #import "UIButton+BeautifulButton.h" @interface NSURLSessionViewController () - (void)layoutUI; @end @implementation NSURLSessionViewController - (void)viewDidLoad { [super viewDidLoad]; [self layoutUI]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)layoutUI { self.navigationItem.title = kTitleOfNSURLSession; self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.000]; [_btnDownloadFile beautifulButton:nil]; } - (IBAction)downloadFile:(id)sender { _lblMessage.text = @"下载中..."; NSString *fileURLStr = kFileURLStr; //编码操作;对应的解码操作是用 stringByRemovingPercentEncoding 方法 fileURLStr = [fileURLStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *fileURL = [NSURL URLWithString:fileURLStr]; //创建请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:fileURL]; //创建会话(这里使用了一个全局会话) NSURLSession *session = [NSURLSession sharedSession]; //创建下载任务,并且启动他;在非主线程中执行 NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { __block void (^updateUI)(); //声明用于主线程更新 UI 的代码块 if (!error) { NSLog(@"下载后的临时保存路径:%@", location); NSString *savePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; savePath = [savePath stringByAppendingPathComponent:_lblFileName.text]; NSURL *saveURL = [NSURL fileURLWithPath:savePath]; NSError *saveError; NSFileManager *fileManager = [NSFileManager defaultManager]; //判断是否存在旧的目标文件,如果存在就先移除;避免无法复制问题 if ([fileManager fileExistsAtPath:savePath]) { [fileManager removeItemAtPath:savePath error:&saveError]; if (saveError) { NSLog(@"移除旧的目标文件失败,错误信息:%@", saveError.localizedDescription); updateUI = ^ { _lblMessage.text = @"下载失败"; }; } } if (!saveError) { //把源文件复制到目标文件,当目标文件存在时,会抛出一个错误到 error 参数指向的对象实例 //方法一(path 不能有 file:// 前缀) // [fileManager copyItemAtPath:[location path] // toPath:savePath // error:&saveError]; //方法二 [fileManager copyItemAtURL:location toURL:saveURL error:&saveError]; if (!saveError) { NSLog(@"保存成功"); updateUI = ^ { _lblMessage.text = @"下载完成"; }; } else { NSLog(@"保存失败,错误信息:%@", saveError.localizedDescription); updateUI = ^ { _lblMessage.text = @"下载失败"; }; } } } else { NSLog(@"下载失败,错误信息:%@", error.localizedDescription); updateUI = ^ { _lblMessage.text = @"下载失败"; }; } dispatch_async(dispatch_get_main_queue(), updateUI); //使用主队列异步方式(主线程)执行更新 UI 的代码块 }]; [downloadTask resume]; //恢复线程,启动任务 } @end
NSURLSessionViewController.xib
[?xml version="1.0" encoding="UTF-8" standalone="no"?] [document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES"] [dependencies] [deployment identifier="iOS"/] [plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/] [/dependencies] [objects] [placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="NSURLSessionViewController"] [connections] [outlet property="btnDownloadFile" destination="mwt-p9-tRE" id="ZVc-6S-ES3"/] [outlet property="lblFileName" destination="dlB-Qn-eOO" id="NdS-9n-7KX"/] [outlet property="lblMessage" destination="qlQ-nM-BXU" id="tRe-SR-AQE"/] [outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/] [/connections] [/placeholder] [placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/] [view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT"] [rect key="frame" x="0.0" y="0.0" width="600" height="600"/] [autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/] [subviews] [label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="metro_demo使用Highcharts实现图表展示.zip" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dlB-Qn-eOO"] [rect key="frame" x="145" y="104" width="309.5" height="18"/] [fontDescription key="fontDescription" type="boldSystem" pointSize="15"/] [color key="textColor" cocoaTouchSystemColor="darkTextColor"/] [nil key="highlightedColor"/] [/label] [button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mwt-p9-tRE"] [rect key="frame" x="250" y="520" width="100" height="40"/] [constraints] [constraint firstAttribute="width" constant="100" id="I5D-tA-ffH"/] [constraint firstAttribute="height" constant="40" id="Y8C-D4-IVr"/] [/constraints] [state key="normal" title="下载"] [color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/] [/state] [connections] [action selector="downloadFile:" destination="-1" eventType="touchUpInside" id="MK4-Yk-IOk"/] [/connections] [/button] [label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qlQ-nM-BXU"] [rect key="frame" x="145" y="140" width="37.5" height="18"/] [fontDescription key="fontDescription" type="system" pointSize="15"/] [color key="textColor" red="0.0" green="0.50196081399917603" blue="1" alpha="1" colorSpace="calibratedRGB"/] [nil key="highlightedColor"/] [userDefinedRuntimeAttributes] [userDefinedRuntimeAttribute type="string" keyPath="text" value=""/] [/userDefinedRuntimeAttributes] [/label] [/subviews] [color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/] [constraints] [constraint firstAttribute="centerX" secondItem="dlB-Qn-eOO" secondAttribute="centerX" id="gNK-NO-rth"/] [constraint firstItem="dlB-Qn-eOO" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="104" id="hwU-O2-Fed"/] [constraint firstAttribute="centerX" secondItem="mwt-p9-tRE" secondAttribute="centerX" id="teN-3t-8Gc"/] [constraint firstItem="qlQ-nM-BXU" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="140" id="w3g-ej-P18"/] [constraint firstItem="dlB-Qn-eOO" firstAttribute="leading" secondItem="qlQ-nM-BXU" secondAttribute="leading" constant="0.5" id="wMU-pU-z9f"/] [constraint firstAttribute="bottom" secondItem="mwt-p9-tRE" secondAttribute="bottom" constant="40" id="yBq-He-Jv8"/] [/constraints] [/view] [/objects] [/document]
NSURLSessionDelegateViewController.h
#import @interface NSURLSessionDelegateViewController : UIViewController @property (strong, nonatomic) NSURLSessionDownloadTask *downloadTask; @property (strong, nonatomic) IBOutlet UILabel *lblFileName; @property (strong, nonatomic) IBOutlet UIProgressView *progVDownloadFile; @property (strong, nonatomic) IBOutlet UILabel *lblMessage; @property (strong, nonatomic) IBOutlet UIButton *btnDownloadFile; @property (strong, nonatomic) IBOutlet UIButton *btnCancel; @property (strong, nonatomic) IBOutlet UIButton *btnSuspend; @property (strong, nonatomic) IBOutlet UIButton *btnResume; @end
NSURLSessionDelegateViewController.m
#import "NSURLSessionDelegateViewController.h" #import "UIButton+BeautifulButton.h" @interface NSURLSessionDelegateViewController () - (void)layoutUI; - (NSURLSession *)defaultSession; - (NSURLSession *)backgroundSession; - (void)updateProgress:(int64_t)receiveDataLength totalDataLength:(int64_t)totalDataLength; @end @implementation NSURLSessionDelegateViewController - (void)viewDidLoad { [super viewDidLoad]; [self layoutUI]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)layoutUI { self.navigationItem.title = kTitleOfNSURLSessionDelegate; self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.000]; [_btnDownloadFile beautifulButton:nil]; [_btnCancel beautifulButton:[UIColor redColor]]; [_btnSuspend beautifulButton:[UIColor purpleColor]]; [_btnResume beautifulButton:[UIColor orangeColor]]; } - (NSURLSession *)defaultSession { /* NSURLSession 支持进程三种会话: 1、defaultSessionConfiguration:进程内会话(默认会话),用硬盘来缓存数据。 2、ephemeralSessionConfiguration:临时的进程内会话(内存),不会将 cookie、缓存储存到本地,只会放到内存中,当应用程序退出后数据也会消失。 3、backgroundSessionConfiguration:后台会话,相比默认会话,该会话会在后台开启一个线程进行网络数据处理。 */ //创建会话配置「进程内会话」 NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; sessionConfiguration.timeoutIntervalForRequest = 60.0; //请求超时时间;默认为60秒 sessionConfiguration.allowsCellularAccess = YES; //是否允许蜂窝网络访问(2G/3G/4G) sessionConfiguration.HTTPMaximumConnectionsPerHost = 4; //限制每次最多连接数;在 iOS 中默认值为4 //创建会话 NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil]; return session; } - (NSURLSession *)backgroundSession { static NSURLSession *session; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //应用程序生命周期内,只执行一次;保证只有一个「后台会话」 //创建会话配置「后台会话」 NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"KMDownloadFile.NSURLSessionDelegateViewController"]; sessionConfiguration.timeoutIntervalForRequest = 60.0; //请求超时时间;默认为60秒 sessionConfiguration.allowsCellularAccess = YES; //是否允许蜂窝网络访问(2G/3G/4G) sessionConfiguration.HTTPMaximumConnectionsPerHost = 4; //限制每次最多连接数;在 iOS 中默认值为4 sessionConfiguration.discretionary = YES; //是否自动选择最佳网络访问,仅对「后台会话」有效 //创建会话 session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil]; }); return session; } - (void)updateProgress:(int64_t)receiveDataLength totalDataLength:(int64_t)totalDataLength; { dispatch_async(dispatch_get_main_queue(), ^{ //使用主队列异步方式(主线程)执行更新 UI 操作 if (receiveDataLength == totalDataLength) { _lblMessage.text = @"下载完成"; kApplication.networkActivityIndicatorVisible = NO; } else { _lblMessage.text = @"下载中..."; kApplication.networkActivityIndicatorVisible = YES; _progVDownloadFile.progress = (float)receiveDataLength / totalDataLength; } }); } - (IBAction)downloadFile:(id)sender { NSString *fileURLStr = kFileURLStr; //编码操作;对应的解码操作是用 stringByRemovingPercentEncoding 方法 fileURLStr = [fileURLStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *fileURL = [NSURL URLWithString:fileURLStr]; //创建请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:fileURL]; //创建会话「进程内会话」;如要用「后台会话」就使用自定义的[self backgroundSession] 方法 NSURLSession *session = [self defaultSession]; //创建下载任务,并且启动他;在非主线程中执行 _downloadTask = [session downloadTaskWithRequest:request]; [_downloadTask resume]; /* 会话任务状态 typedef NS_ENUM(NSInteger, NSURLSessionTaskState) { NSURLSessionTaskStateRunning = 0, //正在执行 NSURLSessionTaskStateSuspended = 1, //已挂起 NSURLSessionTaskStateCanceling = 2, //正在取消 NSURLSessionTaskStateCompleted = 3, //已完成 } NS_ENUM_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0); */ } - (IBAction)cancel:(id)sender { [_downloadTask cancel]; } - (IBAction)suspend:(id)sender { [_downloadTask suspend]; kApplication.networkActivityIndicatorVisible = NO; } - (IBAction)resume:(id)sender { [_downloadTask resume]; } #pragma mark - NSURLSessionDownloadDelegate - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { NSLog(@"已经接收到响应数据,数据长度为%lld字节...", totalBytesWritten); [self updateProgress:totalBytesWritten totalDataLength:totalBytesExpectedToWrite]; } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { //下载文件会临时保存,正常流程下系统最终会自动清除此临时文件;保存路径目录根据会话类型而有所不同: //「进程内会话(默认会话)」和「临时的进程内会话(内存)」,路径目录为:/tmp,可以通过 NSTemporaryDirectory() 方法获取 //「后台会话」,路径目录为:/Library/Caches/com.apple.nsurlsessiond/Downloads/com.kenmu.KMDownloadFile NSLog(@"已经接收完所有响应数据,下载后的临时保存路径:%@", location); __block void (^updateUI)(); //声明用于主线程更新 UI 的代码块 NSString *savePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; savePath = [savePath stringByAppendingPathComponent:_lblFileName.text]; NSURL *saveURL = [NSURL fileURLWithPath:savePath]; NSError *saveError; NSFileManager *fileManager = [NSFileManager defaultManager]; //判断是否存在旧的目标文件,如果存在就先移除;避免无法复制问题 if ([fileManager fileExistsAtPath:savePath]) { [fileManager removeItemAtPath:savePath error:&saveError]; if (saveError) { NSLog(@"移除旧的目标文件失败,错误信息:%@", saveError.localizedDescription); updateUI = ^ { _lblMessage.text = @"下载失败"; }; } } if (!saveError) { //把源文件复制到目标文件,当目标文件存在时,会抛出一个错误到 error 参数指向的对象实例 //方法一(path 不能有 file:// 前缀) // [fileManager copyItemAtPath:[location path] // toPath:savePath // error:&saveError]; //方法二 [fileManager copyItemAtURL:location toURL:saveURL error:&saveError]; if (!saveError) { NSLog(@"保存成功"); updateUI = ^ { _lblMessage.text = @"下载完成"; }; } else { NSLog(@"保存失败,错误信息:%@", saveError.localizedDescription); updateUI = ^ { _lblMessage.text = @"下载失败"; }; } } dispatch_async(dispatch_get_main_queue(), updateUI); //使用主队列异步方式(主线程)执行更新 UI 的代码块 } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { NSLog(@"无论下载成功还是失败,最终都会执行一次"); if (error) { NSString *desc = error.localizedDescription; NSLog(@"下载失败,错误信息:%@", desc); dispatch_async(dispatch_get_main_queue(), ^{ //使用主队列异步方式(主线程)执行更新 UI 操作 _lblMessage.text = [desc isEqualToString:@"cancelled"] ? @"下载已取消" : @"下载失败"; kApplication.networkActivityIndicatorVisible = NO; _progVDownloadFile.progress = 0.0; }); } } @end
NSURLSessionDelegateViewController.xib
[?xml version="1.0" encoding="UTF-8" standalone="no"?] [document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES"] [dependencies] [deployment identifier="iOS"/] [plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/] [/dependencies] [objects] [placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="NSURLSessionDelegateViewController"] [connections] [outlet property="btnCancel" destination="yMY-kU-iKL" id="QHP-ls-q8P"/] [outlet property="btnDownloadFile" destination="mwt-p9-tRE" id="ZVc-6S-ES3"/] [outlet property="btnResume" destination="YSM-n6-UM4" id="RyL-54-rFB"/] [outlet property="btnSuspend" destination="5kz-pB-9nK" id="1Jj-zV-DXM"/] [outlet property="lblFileName" destination="dlB-Qn-eOO" id="vJk-jh-Y2c"/] [outlet property="lblMessage" destination="qlQ-nM-BXU" id="tRe-SR-AQE"/] [outlet property="progVDownloadFile" destination="Me3-m2-iC4" id="PtK-m7-j5N"/] [outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/] [/connections] [/placeholder] [placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/] [view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT"] [rect key="frame" x="0.0" y="0.0" width="600" height="600"/] [autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/] [subviews] [label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="metro_demo使用Highcharts实现图表展示.zip" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dlB-Qn-eOO"] [rect key="frame" x="145" y="104" width="309.5" height="18"/] [fontDescription key="fontDescription" type="boldSystem" pointSize="15"/] [color key="textColor" cocoaTouchSystemColor="darkTextColor"/] [nil key="highlightedColor"/] [/label] [button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mwt-p9-tRE"] [rect key="frame" x="145" y="520" width="70" height="40"/] [constraints] [constraint firstAttribute="width" constant="70" id="I5D-tA-ffH"/] [constraint firstAttribute="height" constant="40" id="Y8C-D4-IVr"/] [/constraints] [state key="normal" title="下载"] [color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/] [/state] [connections] [action selector="downloadFile:" destination="-1" eventType="touchUpInside" id="iGc-6N-bsZ"/] [/connections] [/button] [progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Me3-m2-iC4"] [rect key="frame" x="145" y="160" width="310" height="2"/] [constraints] [constraint firstAttribute="height" constant="2" id="I50-Zx-DwT"/] [constraint firstAttribute="width" constant="310" id="wdS-eD-Tkc"/] [/constraints] [/progressView] [label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qlQ-nM-BXU"] [rect key="frame" x="145" y="180" width="37.5" height="18"/] [fontDescription key="fontDescription" type="system" pointSize="15"/] [color key="textColor" red="0.0" green="0.50196081399917603" blue="1" alpha="1" colorSpace="calibratedRGB"/] [nil key="highlightedColor"/] [userDefinedRuntimeAttributes] [userDefinedRuntimeAttribute type="string" keyPath="text" value=""/] [/userDefinedRuntimeAttributes] [/label] [button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5kz-pB-9nK"] [rect key="frame" x="305" y="520" width="70" height="40"/] [constraints] [constraint firstAttribute="width" constant="70" id="IOm-ve-DPG"/] [constraint firstAttribute="height" constant="40" id="Kwn-EW-gDl"/] [/constraints] [state key="normal" title="挂起"] [color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/] [/state] [connections] [action selector="suspend:" destination="-1" eventType="touchUpInside" id="O6j-t2-7Lv"/] [/connections] [/button] [button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="YSM-n6-UM4"] [rect key="frame" x="385" y="520" width="70" height="40"/] [constraints] [constraint firstAttribute="width" constant="70" id="LhS-5f-cG4"/] [constraint firstAttribute="height" constant="40" id="kzz-1h-4DP"/] [/constraints] [state key="normal" title="恢复"] [color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/] [/state] [connections] [action selector="resume:" destination="-1" eventType="touchUpInside" id="ms9-R9-B9B"/] [/connections] [/button] [button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yMY-kU-iKL"] [rect key="frame" x="225" y="520" width="70" height="40"/] [constraints] [constraint firstAttribute="height" constant="40" id="S7b-Pl-qKI"/] [constraint firstAttribute="width" constant="70" id="gY7-vp-PUz"/] [/constraints] [state key="normal" title="取消"] [color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/] [/state] [connections] [action selector="cancel:" destination="-1" eventType="touchUpInside" id="ITC-zg-bfP"/] [/connections] [/button] [/subviews] [color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/] [constraints] [constraint firstItem="5kz-pB-9nK" firstAttribute="centerY" secondItem="YSM-n6-UM4" secondAttribute="centerY" id="4zt-gy-k65"/] [constraint firstItem="qlQ-nM-BXU" firstAttribute="leading" secondItem="mwt-p9-tRE" secondAttribute="leading" id="RYu-qM-O8P"/] [constraint firstAttribute="centerX" secondItem="Me3-m2-iC4" secondAttribute="centerX" id="Ya8-bM-TaA"/] [constraint firstItem="Me3-m2-iC4" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="160" id="bOY-B5-is2"/] [constraint firstItem="yMY-kU-iKL" firstAttribute="centerY" secondItem="5kz-pB-9nK" secondAttribute="centerY" id="dBh-1A-sIk"/] [constraint firstItem="YSM-n6-UM4" firstAttribute="leading" secondItem="5kz-pB-9nK" secondAttribute="trailing" constant="10" id="fYW-Jv-ro2"/] [constraint firstAttribute="centerX" secondItem="dlB-Qn-eOO" secondAttribute="centerX" id="gNK-NO-rth"/] [constraint firstItem="dlB-Qn-eOO" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="104" id="hwU-O2-Fed"/] [constraint firstItem="mwt-p9-tRE" firstAttribute="centerY" secondItem="yMY-kU-iKL" secondAttribute="centerY" id="lGv-fH-Fh7"/] [constraint firstItem="qlQ-nM-BXU" firstAttribute="leading" secondItem="Me3-m2-iC4" secondAttribute="leading" id="lus-oi-9SA"/] [constraint firstItem="5kz-pB-9nK" firstAttribute="leading" secondItem="yMY-kU-iKL" secondAttribute="trailing" constant="10" id="oge-T7-1td"/] [constraint firstItem="qlQ-nM-BXU" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="180" id="w3g-ej-P18"/] [constraint firstItem="yMY-kU-iKL" firstAttribute="leading" secondItem="mwt-p9-tRE" secondAttribute="trailing" constant="10" id="xCX-1F-xOv"/] [constraint firstAttribute="bottom" secondItem="mwt-p9-tRE" secondAttribute="bottom" constant="40" id="yBq-He-Jv8"/] [/constraints] [/view] [/objects] [/document]
AFNetworkingViewController.h
#import #import "MBProgressHUD.h" @interface AFNetworkingViewController : UIViewController @property (strong, nonatomic) MBProgressHUD *hud; @property (strong, nonatomic) IBOutlet UILabel *lblFileName; @property (strong, nonatomic) IBOutlet UILabel *lblMessage; @property (strong, nonatomic) IBOutlet UIButton *btnDownloadFileByConnection; @property (strong, nonatomic) IBOutlet UIButton *btnDownloadFileBySession; @end
AFNetworkingViewController.m
#import "AFNetworkingViewController.h" #import "AFNetworking.h" #import "AFNetworkActivityIndicatorManager.h" #import "UIButton+BeautifulButton.h" @interface AFNetworkingViewController () - (void)showAlert:(NSString *)msg; - (void)checkNetwork; - (void)layoutUI; - (NSMutableURLRequest *)downloadRequest; - (NSURL *)saveURL:(NSURLResponse *)response deleteExistFile:(BOOL)deleteExistFile; - (void)updateProgress:(int64_t)receiveDataLength totalDataLength:(int64_t)totalDataLength; @end @implementation AFNetworkingViewController - (void)viewDidLoad { [super viewDidLoad]; [self layoutUI]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)showAlert:(NSString *)msg { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"网络情况" message:msg delegate:self cancelButtonTitle:nil otherButtonTitles:@"确定", nil]; [alert show]; } - (void)checkNetwork { NSURL *baseURL = [NSURL URLWithString:@"http://www.baidu.com/"]; AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL]; NSOperationQueue *operationQueue = manager.operationQueue; [manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { switch (status) { case AFNetworkReachabilityStatusReachableViaWiFi: [self showAlert:@"Wi-Fi 网络下"]; [operationQueue setSuspended:NO]; break; case AFNetworkReachabilityStatusReachableViaWWAN: [self showAlert:@"2G/3G/4G 蜂窝移动网络下"]; [operationQueue setSuspended:YES]; break; case AFNetworkReachabilityStatusNotReachable: default: [self showAlert:@"未连接网络"]; [operationQueue setSuspended:YES]; break; } }]; [manager.reachabilityManager startMonitoring]; } - (void)layoutUI { self.navigationItem.title = kTitleOfAFNetworking; self.view.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.000]; //条件表达式中,「?:」可以表示在条件不成立的情况,才使用后者赋值,否则使用用于条件判断的前者赋值 //以下语句等同于:UIButton *btn = _btnDownloadFileByConnection ? _btnDownloadFileByConnection : [UIButton new]; //在 .NET 中,相当于使用「??」;在 JavaScript 中,相当于使用「||」来实现这种类似的判断 UIButton *btn = _btnDownloadFileByConnection ?: [UIButton new]; [btn beautifulButton:nil]; [_btnDownloadFileBySession beautifulButton:[UIColor orangeColor]]; //进度效果 _hud = [[MBProgressHUD alloc] initWithView:self.view]; _hud.mode = MBProgressHUDModeDeterminate; _hud.labelText = @"下载中..."; [_hud hide:YES]; [self.view addSubview:_hud]; //检查网络情况 [self checkNetwork]; //启动网络活动指示器;会根据网络交互情况,实时显示或隐藏网络活动指示器;他通过「通知与消息机制」来实现 [UIApplication sharedApplication].networkActivityIndicatorVisible 的控制 [AFNetworkActivityIndicatorManager sharedManager].enabled = YES; } - (NSMutableURLRequest *)downloadRequest { NSString *fileURLStr = kFileURLStr; //编码操作;对应的解码操作是用 stringByRemovingPercentEncoding 方法 fileURLStr = [fileURLStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; NSURL *fileURL = [NSURL URLWithString:fileURLStr]; //创建请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:fileURL]; return request; } - (NSURL *)saveURL:(NSURLResponse *)response deleteExistFile:(BOOL)deleteExistFile { NSString *fileName = response ? [response suggestedFilename] : _lblFileName.text; //方法一 // NSString *savePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; // savePath = [savePath stringByAppendingPathComponent:fileName]; // NSURL *saveURL = [NSURL fileURLWithPath:savePath]; //方法二 NSURL *saveURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil]; saveURL = [saveURL URLByAppendingPathComponent:fileName]; NSString *savePath = [saveURL path]; if (deleteExistFile) { NSError *saveError; NSFileManager *fileManager = [NSFileManager defaultManager]; //判断是否存在旧的目标文件,如果存在就先移除;避免无法复制问题 if ([fileManager fileExistsAtPath:savePath]) { [fileManager removeItemAtPath:savePath error:&saveError]; if (saveError) { NSLog(@"移除旧的目标文件失败,错误信息:%@", saveError.localizedDescription); } } } return saveURL; } - (void)updateProgress:(int64_t)receiveDataLength totalDataLength:(int64_t)totalDataLength; { dispatch_async(dispatch_get_main_queue(), ^{ //使用主队列异步方式(主线程)执行更新 UI 操作 _hud.progress = (float)receiveDataLength / totalDataLength; if (receiveDataLength == totalDataLength) { _lblMessage.text = receiveDataLength < 0 ? @"下载失败" : @"下载完成"; //kApplication.networkActivityIndicatorVisible = NO; [_hud hide:YES]; } else { _lblMessage.text = @"下载中..."; //kApplication.networkActivityIndicatorVisible = YES; [_hud show:YES]; } }); } - (IBAction)downloadFileByConnection:(id)sender { //创建请求 NSMutableURLRequest *request = [self downloadRequest]; //创建请求操作 AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; NSString *savePath = [[self saveURL:nil deleteExistFile:NO] path]; [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) { NSLog(@"已经接收到响应数据,数据长度为%lld字节...", totalBytesRead); [self updateProgress:totalBytesRead totalDataLength:totalBytesExpectedToRead]; }]; [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@"已经接收完所有响应数据"); NSData *data = (NSData *)responseObject; [data writeToFile:savePath atomically:YES]; //responseObject 的对象类型是 NSData [self updateProgress:100 totalDataLength:100]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"下载失败,错误信息:%@", error.localizedDescription); [self updateProgress:-1 totalDataLength:-1]; }]; //启动请求操作 [operation start]; } - (IBAction)downloadFileBySession:(id)sender { //创建请求 NSMutableURLRequest *request = [self downloadRequest]; //创建会话配置「进程内会话」 NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; sessionConfiguration.timeoutIntervalForRequest = 60.0; //请求超时时间;默认为60秒 sessionConfiguration.allowsCellularAccess = YES; //是否允许蜂窝网络访问(2G/3G/4G) sessionConfiguration.HTTPMaximumConnectionsPerHost = 4; //限制每次最多连接数;在 iOS 中默认值为4 //创建会话管理器 AFURLSessionManager *sessionManager = [[AFURLSessionManager alloc] initWithSessionConfiguration:sessionConfiguration]; //创建会话下载任务,并且启动他;在非主线程中执行 NSURLSessionDownloadTask *task = [sessionManager downloadTaskWithRequest:request progress:nil destination:^ NSURL*(NSURL *targetPath, NSURLResponse *response) { //当 sessionManager 调用 setDownloadTaskDidFinishDownloadingBlock: 方法,并且方法代码块返回值不为 nil 时(优先级高),下面的两句代码是不执行的(优先级低) NSLog(@"下载后的临时保存路径:%@", targetPath); return [self saveURL:response deleteExistFile:YES]; } completionHandler:^ (NSURLResponse *response, NSURL *filePath, NSError *error) { if (!error) { NSLog(@"下载后的保存路径:%@", filePath); //为上面代码块返回的路径 [self updateProgress:100 totalDataLength:100]; } else { NSLog(@"下载失败,错误信息:%@", error.localizedDescription); [self updateProgress:-1 totalDataLength:-1]; } [_hud hide:YES]; }]; //类似 NSURLSessionDownloadDelegate 的方法操作 //- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite; [sessionManager setDownloadTaskDidWriteDataBlock:^ (NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) { NSLog(@"已经接收到响应数据,数据长度为%lld字节...", totalBytesWritten); [self updateProgress:totalBytesWritten totalDataLength:totalBytesExpectedToWrite]; }]; //- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location; [sessionManager setDownloadTaskDidFinishDownloadingBlock:^ NSURL*(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location) { NSLog(@"已经接收完所有响应数据,下载后的临时保存路径:%@", location); return [self saveURL:nil deleteExistFile:YES]; }]; [task resume]; } @end
AFNetworkingViewController.xib
[?xml version="1.0" encoding="UTF-8" standalone="no"?] [document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES"] [dependencies] [deployment identifier="iOS"/] [plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/] [/dependencies] [objects] [placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AFNetworkingViewController"] [connections] [outlet property="btnDownloadFileByConnection" destination="IkH-un-SOz" id="gDd-6X-uxU"/] [outlet property="btnDownloadFileBySession" destination="mwt-p9-tRE" id="5Qk-Zm-V3w"/] [outlet property="lblFileName" destination="dlB-Qn-eOO" id="NdS-9n-7KX"/] [outlet property="lblMessage" destination="qlQ-nM-BXU" id="tRe-SR-AQE"/] [outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/] [/connections] [/placeholder] [placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/] [view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT"] [rect key="frame" x="0.0" y="0.0" width="600" height="600"/] [autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/] [subviews] [label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="metro_demo使用Highcharts实现图表展示.zip" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dlB-Qn-eOO"] [rect key="frame" x="145" y="104" width="309.5" height="18"/] [fontDescription key="fontDescription" type="boldSystem" pointSize="15"/] [color key="textColor" cocoaTouchSystemColor="darkTextColor"/] [nil key="highlightedColor"/] [/label] [button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mwt-p9-tRE"] [rect key="frame" x="175" y="520" width="250" height="40"/] [constraints] [constraint firstAttribute="width" constant="250" id="I5D-tA-ffH"/] [constraint firstAttribute="height" constant="40" id="Y8C-D4-IVr"/] [/constraints] [state key="normal" title="基于 NSURLSession 的下载"] [color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/] [/state] [connections] [action selector="downloadFileBySession:" destination="-1" eventType="touchUpInside" id="z6s-cq-dag"/] [/connections] [/button] [label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qlQ-nM-BXU"] [rect key="frame" x="145" y="140" width="37.5" height="18"/] [fontDescription key="fontDescription" type="system" pointSize="15"/] [color key="textColor" red="0.0" green="0.50196081399917603" blue="1" alpha="1" colorSpace="calibratedRGB"/] [nil key="highlightedColor"/] [userDefinedRuntimeAttributes] [userDefinedRuntimeAttribute type="string" keyPath="text" value=""/] [/userDefinedRuntimeAttributes] [/label] [button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IkH-un-SOz"] [rect key="frame" x="174" y="460" width="250" height="40"/] [constraints] [constraint firstAttribute="width" constant="250" id="3a7-Og-iWa"/] [constraint firstAttribute="height" constant="40" id="mc0-yK-hWE"/] [/constraints] [state key="normal" title="基于 NSURLConnection 的下载"] [color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/] [/state] [connections] [action selector="downloadFileByConnection:" destination="-1" eventType="touchUpInside" id="1ko-jP-kCo"/] [/connections] [/button] [/subviews] [color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/] [constraints] [constraint firstItem="mwt-p9-tRE" firstAttribute="top" secondItem="IkH-un-SOz" secondAttribute="bottom" constant="20" id="Sye-JW-gux"/] [constraint firstAttribute="centerX" secondItem="dlB-Qn-eOO" secondAttribute="centerX" id="gNK-NO-rth"/] [constraint firstItem="dlB-Qn-eOO" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="104" id="hwU-O2-Fed"/] [constraint firstAttribute="centerX" secondItem="IkH-un-SOz" secondAttribute="centerX" constant="1" id="lF1-Yf-Axs"/] [constraint firstAttribute="centerX" secondItem="mwt-p9-tRE" secondAttribute="centerX" id="teN-3t-8Gc"/] [constraint firstItem="qlQ-nM-BXU" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="140" id="w3g-ej-P18"/] [constraint firstItem="dlB-Qn-eOO" firstAttribute="leading" secondItem="qlQ-nM-BXU" secondAttribute="leading" constant="0.5" id="wMU-pU-z9f"/] [constraint firstAttribute="bottom" secondItem="mwt-p9-tRE" secondAttribute="bottom" constant="40" id="yBq-He-Jv8"/] [/constraints] [/view] [/objects] [/document] View Cod
AppDelegate.h
#import @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) UINavigationController *navigationController; @end
AppDelegate.m
#import "AppDelegate.h" #import "ViewController.h" @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; ViewController *viewController = [[ViewController alloc] initWithSampleNameArray:@[ kTitleOfNSURLConnection, kTitleOfNSURLConnectionDelegate, kTitleOfNSURLSession, kTitleOfNSURLSessionDelegate, kTitleOfAFNetworking]]; _navigationController = [[UINavigationController alloc] initWithRootViewController:viewController]; _window.rootViewController = _navigationController; //[_window addSubview:_navigationController.view]; //当_window.rootViewController关联时,这一句可有可无 [_window makeKeyAndVisible]; return YES; } - (void)applicationWillResignActive:(UIApplication *)application { } - (void)applicationDidEnterBackground:(UIApplication *)application { } - (void)applicationWillEnterForeground:(UIApplication *)application { } - (void)applicationDidBecomeActive:(UIApplication *)application { } - (void)applicationWillTerminate:(UIApplication *)application { } @end