(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