iOS開發(fā)_文件下載一

我們通常說的下載就是將數(shù)據(jù)通過網(wǎng)絡(luò)接口請求下來,然后將數(shù)據(jù)保存為本地文件。在這里實際上我們使用到了兩種技術(shù),第一種是網(wǎng)絡(luò)數(shù)據(jù)請求技術(shù),第二種是數(shù)據(jù)本地持久化技術(shù)。

在iOS開發(fā)中進行網(wǎng)絡(luò)數(shù)據(jù)請求,iOS7.0之前我們使用NSURLConnection來進行網(wǎng)絡(luò)請求,但是在iOS7.0之后Apple團隊對NSURLConnection進行了重構(gòu),并推出了NSURLSession作為替代。相比NSURLConnection,NSURLSession提供了更多的更能,比如:將數(shù)據(jù)下載到內(nèi)存中,將數(shù)據(jù)下載到沙盒中,將數(shù)據(jù)上傳到指定的URL,進行后臺下載等功能。而且iOS9.0之后NSURLConnection也被棄用了,所以現(xiàn)在我們在做網(wǎng)絡(luò)請求時使用NSURLSession就OK了。

數(shù)據(jù)本地持久化就是將數(shù)據(jù)保存到沙盒文件中,iOS使用的是沙盒機制,也就是每一個應(yīng)用都有自己的獨立存儲空間,iOS的應(yīng)用程序只能在為該程序創(chuàng)建的文件系統(tǒng)中讀取文件。默認情況下,每個沙盒會自動生成三個文件夾:Documents, Library 和 tmp。

  1. Documents:蘋果建議將程序中建立的或在程序中瀏覽到的文件數(shù)據(jù)保存在該目錄下,iTunes備份和恢復(fù)的時候會包括此文件夾。一般在項目中我們會將用戶相關(guān)的信息放到該文件夾中,比如:用戶名密碼、用戶聊天記錄、用戶保存的信息等;與用戶操作相關(guān)的也就是不可再生的內(nèi)容,會保存到該文件夾中。
  2. Library:存儲程序的默認設(shè)置或其它狀態(tài)信息;
  3. Library/Caches:存放緩存文件,iTunes不會備份此目錄,此目錄下文件不會在應(yīng)用退出時刪除;在項目中我們會將一些大的圖片、音頻、視頻等文件保存到該文件夾中;像圖片、音頻、視頻等這些可再生的資源一般都放到Caches中。
  4. tmp:提供一個即時創(chuàng)建臨時文件的地方,程序一旦退出就會被清空。

一、小文件的下載

小文件的下載指的是不需要等待很長時間的網(wǎng)絡(luò)數(shù)據(jù)請求,可以是數(shù)據(jù)量比較小的圖片或者其他格式的文件。將數(shù)據(jù)請求完成之后再將數(shù)據(jù)存成本地文件。

1、使用NSURLConnection進行下載

  1. 將數(shù)據(jù)存成本地文件,首先需要考慮將文件存在沙盒的什么位置?
    如果是圖片的話需要存到Library/Caches中去。
    在.m文件中實現(xiàn)一個根據(jù)圖片URL創(chuàng)建圖片路徑的方法:
圖片路徑格式解析
    - (NSString *)imageFilePath:(NSString *)imageUrl {
    // 1、獲取caches文件夾路徑
    NSString * cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    
    // 2、創(chuàng)建DownloadImages文件夾
    NSString * downloadImagesPath = [cachesPath stringByAppendingPathComponent:@"DownloadImages"];

    // 3、創(chuàng)建文件管理器對象
    NSFileManager * fileManager = [NSFileManager defaultManager];
    
    // 4、判斷文件夾是否存在
    if (![fileManager fileExistsAtPath:downloadImagesPath])
    {
        [fileManager createDirectoryAtPath:downloadImagesPath withIntermediateDirectories:YES attributes:nil error:nil];
    }
    
    // 5、拼接圖片在沙盒中的路徑
    /*
      因為每一個圖片URL對應(yīng)的是一張圖片,而且URL中包含了文件的名稱,所以可以用圖片的URL來唯一表示圖片的名稱
      因為圖像URL中有"/","/"表示的是下級目錄的意思,要在存入前替換掉,所以用"_"代替
    */
    NSString * imageName = [imageUrl stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
    NSString * imageFilePath = [downloadImagesPath stringByAppendingPathComponent:imageName];

    // 6、返回文件路徑
    return imageFilePath;
}
  1. 根據(jù)圖片路徑加載本地圖片,在.m文件中實現(xiàn)加載本地圖片的方法:
  - (UIImage *)loadLocalImage:(NSString *)imageUrl {
      // 1、獲取圖片路徑,根據(jù)上一步中創(chuàng)建本地圖片路徑的方法來獲取
      NSString * filePath = [self imageFilePath:imageUrl];
     // 2、根據(jù)本地圖片路徑創(chuàng)建UIImage對象
      UIImage * image = [UIImage imageWithContentsOfFile:filePath];

     // 3、判斷UIImage對象并返回
      if (image != nil) {
        return image;
      }
      return nil;
 }
  1. 創(chuàng)建根據(jù)圖片URL來請求數(shù)據(jù)的方法,該方法是需要外部調(diào)用的,所以在.h文件中要定義接口,供外部調(diào)用;
    .h中完整的實現(xiàn)如下:
    //聲明了一個下載成功的block類型
    typedef void (^imageDownLoadSuccess) (NSData *);
    //聲明一個失敗的block類型
    typedef void (^imageDownLoadError) (NSError *);

    @interface ImageDownLoader : NSObject

    @property (nonatomic, copy) imageDownLoadSuccess successBlock;
    @property (nonatomic, copy) imageDownLoadError errorBlock;

    //聲明一個block傳值的請求方法
   /*
    參數(shù)解釋:
    imageUrl:圖片的地址;
    successBlock: 當請求成功時進行回調(diào);
    errorBlock:   當請求失敗時進行回調(diào);
   */
    - (void)requestImageUrl:(NSString *)imageUrl
           successBlock:(imageDownLoadSuccess)successBlock
             errorBlock:(imageDownLoadError)errorBlock;
    @end

.m中方法的實現(xiàn)如下:

-(void)requestImageUrl:(NSString *)imageUrl 
          successBlock:(imageDownLoadSuccess)successBlock 
            errorBlock:(imageDownLoadError)errorBlock {
    
     self.successBlock = successBlock;
     self.errorBlock = errorBlock;

      // 下載圖片之前先檢查本地是否已經(jīng)有圖片
      UIImage * image = [self loadLocalImage:imageUrl];
      NSData *imageData = UIImagePNGRepresentation(image);
      //如果圖片存在直接跳出;不用下載了
      if (imageData) {
        self.successBlock(imageData);
        return;
      }
    
    // 沒有本地圖片
    // 創(chuàng)建URL對象
     NSURL *url = [NSURL URLWithString:[imageUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    // 創(chuàng)建request對象
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    // 發(fā)送異步請求
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        // 如果請求到數(shù)據(jù)
        if (data) {
            // 將下載的數(shù)據(jù)傳出去更新UI
            self.successBlock(data);
            // 下載完成,將圖片保存到本地
            [data writeToFile:[self imageFilePath:imageUrl] atomically:YES];
        }
       // 如果有錯誤信息,將錯誤信息返回
        if (connectionError) {
            self.errorBlock(connectionError);
        }
    }];
 } 

2、使用NSURLSession進行下載

使用NSURLSession進行數(shù)據(jù)請求和NSURLConnection的流程基本一致,在請求方法的實現(xiàn)部分做一下修改即可;方法實現(xiàn)的完整代碼如下:

-(void)requestImageUrl:(NSString *)imageUrl successBlock:(imageDownLoadSuccess)successBlock errorBlock:(imageDownLoadError)errorBlock {
    
     self.successBlock = successBlock;
     self.errorBlock = errorBlock;

      // 下載圖片之前先檢查本地是否已經(jīng)有圖片
      UIImage * image = [self loadLocalImage:imageUrl];
      NSData *imageData = UIImagePNGRepresentation(image);
      //如果圖片存在直接跳出;不用下載了
      if (imageData) {
        self.successBlock(imageData);
        return;
      }
    
    // 沒有本地圖片
    // 創(chuàng)建URL對象
     NSURL *url = [NSURL URLWithString:[imageUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    // 創(chuàng)建request對象
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    // 使用URLSession來進行網(wǎng)絡(luò)請求
    // 創(chuàng)建會話配置對象
    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    // 創(chuàng)建會話對象
    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
    // 創(chuàng)建會話任務(wù)對象
    NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
         if (data) {
             // 將下載的數(shù)據(jù)傳出去,進行UI更新
             self.successBlock(data);
             // 下載完成,將圖片保存到本地
             [data writeToFile:[self imageFilePath:imageUrl] atomically:YES];
             
         }
         if (error) {
             self.errorBlock(error);
         }
     }];

    // 創(chuàng)建的task都是掛起狀態(tài),需要resume才能執(zhí)行
    [task resume];
 } 

在使用URLSession進行網(wǎng)絡(luò)請求時,實現(xiàn)步驟一共就兩步:創(chuàng)建一個任務(wù),執(zhí)行任務(wù);
在這兩大步中一共使用到了三個類:NSURLSessionConfiguration、NSURLSession和NSURLSessionTask。
下面我們來對這三個類進行簡單的解釋;

NSURLSessionConfiguration

NSURLSession配置信息,創(chuàng)建配置信息對象時當上傳和下載數(shù)據(jù)時需要做的第一步操作。這些配置信息決定了NSURLSession的種類,HTTP的額外headers,請求的timeout時間,Cookie的接受策略等配置信息。
創(chuàng)建配置信息對象時有三種創(chuàng)建方法:
第一種,默認配置,使用硬盤來存儲緩存數(shù)據(jù)。
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
第二種,臨時配置,與默認配置相比,這個配置不會將緩存、cookie等存在本地,只在內(nèi)存中存在,所以當程序退出時,所有的數(shù)據(jù)都會消失。
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration
第三種,后臺配置,iOS8.0之后可以使用的一種創(chuàng)建配置對象的方法;與默認配置類似,不同的是會在后臺開啟另一個線程來處理網(wǎng)絡(luò)數(shù)據(jù)。當進行后臺下載時可以使用該方法來創(chuàng)建配置對象。
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier;

NSURLSession

由Configuration對象來進行配置,然后通過Session對象來創(chuàng)建NSURLSessionTask。
創(chuàng)建NSURLSession對象的方法有三種:
1、不需要自己創(chuàng)建Configuration對象,用于一般的網(wǎng)絡(luò)數(shù)據(jù)請求
+ (NSURLSession *)sharedSession;
2、需要自己創(chuàng)建Configuration對象,在上傳和下載功能中使用;并且使用該方法時不能使用使用協(xié)議方法來監(jiān)控網(wǎng)絡(luò)狀態(tài)
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
3、需要自己創(chuàng)建Configuration對象,在上傳和下載功能中使用;在該方法中可以設(shè)置代理對象,可以使用協(xié)議方法來監(jiān)控網(wǎng)絡(luò)狀態(tài)
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;

NSURLSessionTask

NSURLSessionTask是一個抽象類,不能直接使用;使用的是它的子類,一共有4個子類:
NSURLSessionDataTask 數(shù)據(jù)請求任務(wù)
NSURLSessionDownloadTask 文件下載任務(wù)
NSURLSessionUploadTask 文件上傳任務(wù)
NSURLSessionStreamTask 文件流任務(wù)
關(guān)于NSURLSessionTask的使用我們會在后面的部分中再去詳細解釋。

附1:

swift版,使用NSURLConnection實現(xiàn)下載的代碼:

import UIKit

//請求成功
typealias imageDownLoadSuccess = (NSData) -> ();
//請求失敗
typealias imageDownLoadError = (NSError) -> ();

class ImageDownLoader: NSObject {
    
    var successBlock : imageDownLoadSuccess!
    var errorBlock : imageDownLoadError!
    
    
    //  請求圖片
    func requestImageUrl(imageUrl : NSString, successBlock : imageDownLoadSuccess, errorBlock : imageDownLoadError) {
        
        self.successBlock = successBlock;
        self.errorBlock = errorBlock;
        
        //  判斷沙盒中是否有緩存圖片
        let loadImage = self.loadLocalImage(imageUrl) as UIImage?
        
        if((loadImage) != nil) {
            let imageData = UIImagePNGRepresentation(loadImage!)
            self.successBlock(imageData!)
            return;
        }
        
        //  創(chuàng)建url對象
        let url = NSURL(string: imageUrl.stringByRemovingPercentEncoding!)
        //  創(chuàng)建request對象
        let request = NSURLRequest(URL: url!);
        //  發(fā)送異步請求
        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response, data, connectionError) -> Void in
            if (data != nil) {
                // 回調(diào)更新UI
                self.successBlock(data!)
                // data寫入沙盒
                data?.writeToFile(self.imageFilePath(imageUrl), atomically: true)
            }
            if (connectionError != nil){
                self.errorBlock(connectionError!)
            }
        }
    }
    
    //  本地圖片
    func loadLocalImage(imageUrl : NSString) -> UIImage?
    {
        let filePath = self.imageFilePath(imageUrl)
        let image = UIImage(contentsOfFile: filePath) as UIImage?
        return image
    }
    
    //  獲取圖片沙盒路徑
    func imageFilePath(imageUrl : NSString) -> String {
        let cachesPath: AnyObject? = (NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true) as NSArray).lastObject
        
        let downloadImagesPath : String = cachesPath!.stringByAppendingPathComponent("DownloadImages")
        
        let fileManager = NSFileManager.defaultManager()
        
        if (!fileManager.fileExistsAtPath(downloadImagesPath)) {
            
        do{
            try fileManager.createDirectoryAtPath(downloadImagesPath, withIntermediateDirectories: true, attributes: nil) }
            catch _{
                
            }
        }
        
        let imageName = imageUrl.stringByReplacingOccurrencesOfString("/", withString: "_")
        
        let imageFilePath = (downloadImagesPath as NSString).stringByAppendingPathComponent(imageName as String)
        
        return imageFilePath
    }
}

附2:

swift版,使用NSURLSession實現(xiàn)下載的代碼:
只將請求的方法進行附錄,其他內(nèi)容與附1中相同;

//  請求圖片
    func requestImageUrl(imageUrl : NSString, successBlock : imageDownLoadSuccess, errorBlock : imageDownLoadError) {
        
        self.successBlock = successBlock;
        self.errorBlock = errorBlock;
        
        //  判斷沙盒中是否有緩存圖片
        let loadImage = self.loadLocalImage(imageUrl) as UIImage?
        
        if((loadImage) != nil) {
            let imageData = UIImagePNGRepresentation(loadImage!)
            self.successBlock(imageData!)
            return;
        }
        
        //  創(chuàng)建url對象
        let url = NSURL(string: imageUrl.stringByRemovingPercentEncoding!)
        //  創(chuàng)建request對象
        let request = NSURLRequest(URL: url!);
        
        
        let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration();
        
        let session = NSURLSession(configuration: sessionConfiguration);
        
        let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
            if (data != nil) {
                // 回調(diào)更新UI
                self.successBlock(data!)
                // data寫入沙盒
                data?.writeToFile(self.imageFilePath(imageUrl), atomically: true)
            }
            if ((error) != nil) {
                self.errorBlock(error!);
            }

        }
        task.resume();
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容