標籤:
下載的一些用法
①對於小檔案,可以直接下載,無需斷點下載等處理。
1 -(void)clickDownBtn{ 2 NSURL *url = [NSURL URLWithString:@"https://picjumbo.imgix.net/HNCK8461.jpg?q=40&w=1650&sharp=30"]; 3 if (self.imgView.image == nil) { 4 [self downLoad:url]; 5 } 6 } 7 8 //下載過程 9 -(void)downLoad:(NSURL *)url{10 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{11 NSData *imgData = [NSData dataWithContentsOfURL:url];12 UIImage *img = [UIImage imageWithData:imgData];13 if (imgData != nil) {14 dispatch_sync(dispatch_get_main_queue(), ^{15 self.imgView.image = img;16 });17 }18 });19 }
需要注意的是,多線程中,資料處理是在子線程,UI更新是在主線程。若處理資料時沒有在子線程中進行,那麼會發生線程阻塞、介面卡死的情況。如下載圖片時,
-(void)downLoad:(NSURL *)url{ //資料處理(NSData)是在主線程中進行,所以會卡死 //況且這種寫法是沒有意義的,本來就在主線程,就無需再跳往主線程更新UI NSData *imgData = [NSData dataWithContentsOfURL:url]; UIImage *img = [UIImage imageWithData:imgData]; if (imgData != nil) { dispatch_sync(dispatch_get_main_queue(), ^{ self.imgView.image = img; }); }}
正確的方法是,先開闢子線程,然後在主線程更新UI。
開闢子線程
-(void)downLoad:(NSURL *)url{ //開闢子線程 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //操作 });}
async是非同步,sync是同步
②而對於大檔案,則可以使用NSURLSession實現暫停、斷點下載等操作。如下:
監聽下載進度,需要實現代理NSURLSessionDownloadDelegate,在這代理中常用的有3個方法
/** 下載完畢會調用,location為檔案臨時地址 */-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ }
/** * 每次寫入沙箱完畢後調用,在這裡可以設定進度條資料等操作 * totalBytesWritten/totalBytesExpectedToWrite,這兩個用來監視下載進度 * * @param bytesWritten 這次寫入的大小 * @param totalBytesWritten 已經寫入沙箱的大小 * @param totalBytesExpectedToWrite 檔案總大小 */-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWrittentotalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ }
/** 恢複下載時使用 */-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{ }
範例程式碼如下,在.m中
/** 展示進度的label */@property(nonatomic,strong) UILabel *progressLabel;/** 進度動畫 */@property(nonatomic,strong) UIProgressView *progressView;/** 下載按鈕 */@property(nonatomic,strong) UIButton *downBtn;/** 下載任務 */@property(nonatomic,strong) NSURLSessionDownloadTask *downLoadTask;/** 快取資料 */@property(nonatomic,strong) NSData *tempData;/** session */@property(nonatomic,strong) NSURLSession *session;
session的懶載入為
-(NSURLSession *)session{ if (!_session) { NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration]; _session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]]; } return _session;}
按鈕點擊事件如下:
//點擊下載按鈕-(void)clickDownBtn{ self.downBtn.selected = !self.downBtn.selected; if (nil == self.downLoadTask) { if (self.tempData) { [self goOnDownload]; //繼續下載 } else { //從0開始下載 [self startDownload]; } } else { [self pauseDownload]; }}/** 開始下載 */-(void)startDownload{ NSURL *url = [NSURL URLWithString:@"http://11.gxdx2.crsky.com/201501/apkok-v3.0.zip"]; //建立任務 self.downLoadTask = [self.session downloadTaskWithURL:url]; //開始任務 [self.downLoadTask resume];}/** 暫停下載 */-(void)pauseDownload{ //防止循環參考 __weak typeof(self) weakSelf = self; [self.downLoadTask cancelByProducingResumeData:^(NSData *resumeData) { //resumeData包含了繼續下載的開始位置和url weakSelf.tempData = resumeData; weakSelf.downLoadTask = nil; }];}/** 恢複下載 */-(void)goOnDownload{ //傳入上次暫停下載返回的資料,即可恢複下載 self.downLoadTask = [self.session downloadTaskWithResumeData:self.tempData]; [self.downLoadTask resume]; self.tempData = nil;}
實現2個代理方法,第3個暫時用不到
/** 下載完畢會調用,location為檔案臨時地址 */-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ //選擇下載檔案路徑,檔案只能下載到該程式的目錄中 //下邊為下載到Documents檔案夾,若將NSDocumentDirectory換為NSCachesDirectory,則會下載到Cache檔案夾 NSString *caches = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; //response.suggestedFilename建議使用的檔案名稱,一般跟伺服器端的名稱一致 NSString *file = [caches stringByAppendingPathComponent:downloadTask.response.suggestedFilename]; //將臨時檔案複製或剪下到caches檔案夾 NSFileManager *fileManager = [NSFileManager defaultManager]; //AtPath : 剪下前的檔案路徑 //toPath : 剪下後的檔案路徑 //因為檔案都是下載到臨時檔案夾,下載完成後會刪除,所以必須移位置 [fileManager moveItemAtPath:location.path toPath:file error:nil]; // 提示下載完成 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"下載完成" message:downloadTask.response.suggestedFilename delegate:self cancelButtonTitle:nil otherButtonTitles: nil]; [alert show]; [self performSelector:@selector(dismissAlert:) withObject:alert afterDelay:2]; //將下載按鈕置灰 self.downBtn.selected = NO; self.downBtn.enabled = NO;}/** * 每次寫入沙箱完畢後調用,在這裡可以設定進度條資料等操作 * totalBytesWritten/totalBytesExpectedToWrite,這兩個用來監視下載進度 * * @param bytesWritten 這次寫入的大小 * @param totalBytesWritten 已經寫入沙箱的大小 * @param totalBytesExpectedToWrite 檔案總大小 */-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWrittentotalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ //注意該處的progress,等號右邊必須強制轉換為double型,進度條才有動畫效果,否則只有在下載完成後才有 self.progressView.progress = (float)totalBytesWritten/totalBytesExpectedToWrite; //設定成百分比形式 NSString *percent = [NSString stringWithFormat:@"下載進度 %0.0f%%", (double)totalBytesWritten/totalBytesExpectedToWrite*100]; self.progressLabel.text = percent;}
其中一些點要注意下:
一、
UIProgressView的progress為float型,所以設定時也要為float型,否則不顯示動畫效果
self.progressView.progress = (float)totalBytesWritten/totalBytesExpectedToWrite;
效果而下:
沒有強制轉換
self.progressView.progress = totalBytesWritten/totalBytesExpectedToWrite;
效果而下:
二、
NSSearchPathForDirectoriesInDomains可以用來擷取app的私人檔案路徑,即沙箱中的資料夾清單,擷取的為數組形式。
//擷取Document檔案夾NSString *document = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];//擷取Cache檔案夾NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
IOS斷點下載