iOS原生文件的下載

小文件的下載

NSData直接從URL中加載數(shù)據(jù)

系統(tǒng)提供了方法, 讓很多東西都可以直接轉(zhuǎn)換成二進制數(shù)據(jù)的類型(可以從URL加載,file加載等等,單一數(shù)據(jù),直接寫入)

// 從提供URL中下載數(shù)據(jù)
 NSData *data = [NSData dataWithContentsOfURL:
                    [NSURL URLWithString: @"http://b.hiphotos.baidu.com/zhidao/pic/item/728da9773912b31b31187e8c8418367adab4e11b.jpg"]];
// 數(shù)據(jù)轉(zhuǎn)換成圖片
    UIImage *image = [UIImage imageWithData:data    ];
    self.imageView.image = image; 
    // 將圖片數(shù)據(jù)儿普,轉(zhuǎn)換成data數(shù)據(jù)
    NSData *data1 = UIImageJPEGRepresentation(image, 0);
    // 將data數(shù)據(jù)背镇,進行保存
    [data1 writeToFile:@"/Users/liujiaxin/Desktop/image.jpeg" atomically:YES];
NSURLConnection如果是小文件的話,直接異步請求便能得到相應(yīng)的數(shù)據(jù).

    NSURL *url = [NSURL URLWithString:@"http://www.people.com.cn/mediafile/pic/20150811/19/7260132349722664171.jpg"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
  // 發(fā)送請求
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        // 主隊列回調(diào) 賦值UI
        self.imageView.image = [UIImage imageWithData:data];
    }];
NSURLSession

IOS7以后不建議使用NSURLConnection進行網(wǎng)絡(luò)數(shù)據(jù)處理. [session更加靈活,將操作封裝到任務(wù)中,通過對任務(wù)的操作能夠精確的控制操作]NSURLSession, 相當于處理放在會話層執(zhí)行 (有點大一統(tǒng)的想法, 因為二維碼相關(guān)的也需要會話)
NSURLSessionTask


    /**
     *  步驟
     *  1尘应、創(chuàng)建task(任務(wù))【任務(wù)分為:downloadTask, dataTask(uploadTask)】
     *  2笋额、利用 session 創(chuàng)建 task
     *  3、創(chuàng)建 session
     *  4篷扩、執(zhí)行task   - resume 重新執(zhí)行
     */
    
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    request.HTTPBody = [@"username=520it&pwd=520it" dataUsingEncoding:NSUTF8StringEncoding];
    
    NSURLSession *session = [NSURLSession sharedSession];

    // 可以不必創(chuàng)建請求直接用url進行獲取兄猩,但是只能應(yīng)用于get請求
    
    NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    }];  
    [task resume];
NSURLSessionDownloadTask
NSURLSession *session = [NSURLSession sharedSession];
    
    /**
     *  創(chuàng)建下載任務(wù) (該任務(wù)啟動后便會自己進行下載,并存儲的)
     *
     *  @param NSURL 下載那個url 的任務(wù)
     *
     *  @block 中的數(shù)據(jù) location下載好的文件放在磁盤的位置(會自己創(chuàng)建一個臨時文件夾的臨時目錄)
     *           臨時目錄存放文件鉴未,會不定時的刪除枢冤, 所以最后將文件轉(zhuǎn)移到Cache中
     *    響應(yīng)頭
     */
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"];
    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
        NSFileManager *mgr = [NSFileManager defaultManager];
//        [mgr createFileAtPath:[response.suggestedFilename cacheDir] contents:nil attributes:nil];
        // 將文件原存儲的的地址 ,轉(zhuǎn)換成新的存儲地址铜秆, (將沙盒cache地址 用url 進行轉(zhuǎn)換)淹真。
        [mgr moveItemAtURL:location toURL:[NSURL fileURLWithPath:[response.suggestedFilename cacheDir]] error:nil];
        NSLog(@"%@",[response.suggestedFilename cacheDir]); 
    }];
    [task resume];

大文件下載

NSURLConnection,在網(wǎng)絡(luò)一章中已經(jīng)說明,對于文件的下載上傳管理都是交由代理來完成的.
NSURLConnection的代理方法有:NSURLConnectionDataDelegate
1.接收到響應(yīng)式調(diào)用
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response
2.接收到數(shù)據(jù)時調(diào)用
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data[會調(diào)用多次, 每次接受文件是由限制的. 所以會進行多次調(diào)用,我們需要將每次接受到的數(shù)據(jù)進行拼接]
3.連接失敗時調(diào)用
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
4.接收數(shù)據(jù)完成時調(diào)用
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
在大文件下載之前,我們還需要考慮的是,
1>數(shù)據(jù)放在應(yīng)用中,建立一個容器專門存放數(shù)據(jù),這樣調(diào)用雖然方便,但是數(shù)據(jù)存放在應(yīng)用中,必定會消耗一定的內(nèi)存,而且會消耗一定得性能.將讀取到的數(shù)據(jù),存放在沙盒文件中
2>在沙盒中建立文件,存放數(shù)據(jù).(一邊下載,一邊存放)然后將已經(jīng)保存的數(shù)據(jù)進行讀取就可以了
第一種方案:

// 建立一個容器數(shù)據(jù),用來保存下載后的數(shù)據(jù)(但由于连茧,數(shù)據(jù)一致存在于內(nèi)存核蘸,所以非常耗性能)
@property(strong,nonatomic) NSMutableData *data;
- (NSMutableData *)data
{
    if (!_data) {
        _data = [NSMutableData data];
    }
    return _data;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *urlStr = [ @"http://120.25.226.186:32812" stringByAppendingPathComponent: @"resources/videos/minion_01.mp4"];
    NSURL *url =  [NSURL URLWithString:urlStr];
 // 設(shè)置請求   
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 設(shè)置URLConnection,并且設(shè)置代理   
 NSURLConnection *urlCon = [NSURLConnection connectionWithRequest:request delegate:self];   
}

// 代理方法。開始接受相應(yīng)的時候調(diào)用
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response
{
    // 由協(xié)議頭 取出 接收數(shù)據(jù)的總長度
    self.AllLength = response.expectedContentLength;
}

// 開始接收到數(shù)據(jù)的時候調(diào)用
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{

    // 拼接數(shù)據(jù)
    [self.data appendData:data];   
  // 取出當前文件下載長度
    self.currentLength += data.length; 
    // 得到控件sliderView的值
    self.slideView.value = 1.0 * self.currentLength / self.AllLength; 
}
// 失敗時調(diào)用
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
 NSLog(@"%s",__func__);
    
}

// 完成時調(diào)用
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// 完成時 將拼接數(shù)據(jù)保存
    [self.data writeToFile:[@"minion_01.mp4" cacheDir] atomically:YES];
}

第二種方案:
沙盒建立文件,存放下載數(shù)據(jù)

- (void)viewDidLoad {
    [super viewDidLoad];
   
    NSString *urlStr = [ @"http://120.25.226.186:32812" stringByAppendingPathComponent: @"resources/videos/minion_01.mp4"];
    NSURL *url =  [NSURL URLWithString:urlStr];
    
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLConnection *urlCon = [NSURLConnection connectionWithRequest:request delegate:self];
    
}
// 代理方法啸驯。開始接受相應(yīng)的時候調(diào)用
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response
{
    
    // 由協(xié)議頭 取出 接收數(shù)據(jù)的總長度
    self.AllLength = response.expectedContentLength;
// 只有利用manager 才能建立文件       
 NSFileManager *mgr = [NSFileManager defaultManager];
    // 有協(xié)議頭 得到客扎,接收到文件的名稱
         NSString *filePath = [response.suggestedFilename cacheDir];
        NSLog(@"%@",filePath);
        self.filePath = filePath;
        [mgr createFileAtPath:filePath contents:NULL attributes:NULL];   
}
// 開始接收到數(shù)據(jù)的時候調(diào)用
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{

    // 拼接數(shù)據(jù)
//    [self.data appendData:data];
// 建立文件句柄 --- 主要用于文件數(shù)據(jù)處理   
 NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:self.filePath];
    // 將后一個數(shù)據(jù)寫在前一個數(shù)據(jù)的后面
    [handle seekToEndOfFile];
    // 寫數(shù)據(jù)
    [handle writeData:data];
    // 取出當前文件下載長度
    self.currentLength += data.length;
    // 得到控件sliderView的值
    self.slideView.value = 1.0 * self.currentLength / self.AllLength;    
}
// 失敗時調(diào)用
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
 NSLog(@"%s",__func__);   
}
// 完成時調(diào)用
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// 完成時 將拼接數(shù)據(jù)保存
//    [self.data writeToFile:[@"minion_01.mp4" cacheDir] atomically:YES];
}

第三種情況:當出現(xiàn)網(wǎng)絡(luò)情況很差,或者出現(xiàn)斷網(wǎng)的情況,這樣的話,如果下載到99.9%,然后斷網(wǎng),在進行下載的話,是不是很坑呢.....
所以應(yīng)該我們需要進行斷點續(xù)傳
斷點續(xù)傳的實現(xiàn).
協(xié)議頭,我們需要傳入這么一個參數(shù)Range: 將這么一個參數(shù)設(shè)置的話, 就想到與告訴服務(wù)請,請求的數(shù)據(jù)的范圍從什么地方開始,所以我們?nèi)绻嬖V服務(wù)器范圍(文件已經(jīng)下載的大小),就可以了

- (IBAction)btnClick:(UIButton *)sender {
    // 1.切換按鈕圖片
    sender.selected = !sender.selected;
    // 2.判斷是否是繼續(xù)下載
    if (sender.selected) {
        // 繼續(xù)下載
        NSLog(@"繼續(xù)下載");
        NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"];
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        
        // 只要設(shè)置HTTP請求頭的Range屬性, 就可以實現(xiàn)從指定位置開始下載
        /*
         表示頭500個字節(jié):Range: bytes=0-499
         表示第二個500字節(jié):Range: bytes=500-999
         表示最后500個字節(jié):Range: bytes=-500
         表示500字節(jié)以后的范圍:Range: bytes=500-
         */
        NSString *range = [NSString stringWithFormat:@"bytes %zd-", self.currentLength]; 
        [request setValue:range forHTTPHeaderField:@"Range"];
        
        self.con = [NSURLConnection connectionWithRequest:request delegate:self];
    }else
    {
        // 暫停
        NSLog(@"暫停");
        [self.con cancel];
    }
}

// 接收到服務(wù)器的響應(yīng)
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response
{
    self.totalLength = response.expectedContentLength;
    // 先去文件中獲取下載進度
    
    // 并不是每次都需要創(chuàng)建文件
    // 如果已經(jīng)下載了一部分, 就繼續(xù)在上一個文件的后面添加
    if (self.currentLength > 0) {
        return;
    }
    
    
    // 創(chuàng)建容器
    // 創(chuàng)建一個空的文件, 用于保存下載的數(shù)據(jù)
    NSFileManager *manager = [NSFileManager defaultManager];
    NSString *path = [response.suggestedFilename cacheDir];
    NSLog(@"path = %@", path);
    [manager createFileAtPath:path contents:nil attributes:nil];
    self.path = path;
}

// 接收到服務(wù)器返回的數(shù)據(jù)
// 會調(diào)用一次或多次
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{

    // 1.創(chuàng)建一個操作文件的句柄
//    NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:self.path];
    
    // 設(shè)置數(shù)據(jù)寫入的位置
    // 只要調(diào)用這個方法, 每次寫入數(shù)據(jù)都會寫入到上一次的后面
    [self.handle seekToEndOfFile];
    
    // 2.利用句柄往文件中寫入數(shù)據(jù)
    [self.handle writeData:data];
    
    // 3.計算當前接收到得數(shù)據(jù)的總數(shù)
    self.currentLength += data.length;
    
    // 計算下載比例
    self.progressView.progress = 1.0 * self.currentLength/self.totalLength;
}

// 接收完畢
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    [self.handle closeFile];
    self.handle = nil;
    
    NSLog(@"下載完畢");
}
#pragma mark - lazy 
- (NSFileHandle *)handle
{
    if (!_handle) {
        // 1.創(chuàng)建一個操作文件的句柄
        _handle = [NSFileHandle fileHandleForWritingAtPath:self.path];
    }
    return _handle;
}

第四種方式 : 句柄 (太陌生), 輸出數(shù)據(jù)流進行數(shù)據(jù)的存儲(根據(jù)文件創(chuàng)建數(shù)據(jù)輸出流, 數(shù)據(jù)流,只接受byte數(shù)據(jù) )


// 設(shè)置按鈕的監(jiān)聽
- (IBAction)clickBtn:(UIButton *)sender;
// 創(chuàng)建網(wǎng)絡(luò)請求連接
@property (strong, nonatomic) NSURLConnection *con;
// 記錄接收文件的總大小
@property (assign, nonatomic) NSInteger totalLength;
// 記錄接收到的當前文件的大小
@property (assign, nonatomic) NSInteger currentLength;
// 記錄文件存儲路徑
@property (copy, nonatomic) NSString *toPath;
// 設(shè)置輸出流,用于接收數(shù)據(jù)
@property (strong, nonatomic)  NSOutputStream *stream;
@end

@implementation ViewController

- (IBAction)clickBtn:(UIButton *)sender {
    
    sender.selected = !sender.selected;

    if (sender.selected) {
        
        NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"];
        
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        // 拼接請求頭
        NSString *range = [NSString stringWithFormat:@"bytes %zd-", self.currentLength];
        [request setValue:range forHTTPHeaderField:@"Range"];
        
      self.con = [NSURLConnection connectionWithRequest:request delegate:self];
        
    }else{
        // 暫停連接
        [self.con cancel];
    
    }
    
}
// 接收到響應(yīng)
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    self.totalLength = response.expectedContentLength;
    // 如果有數(shù)據(jù)便不在創(chuàng)建新的文件
    if (self.currentLength > 0) {
        return;
    }
    NSFileManager *mgr = [NSFileManager defaultManager];
    // suggestFilename:就是取出響應(yīng)頭中 記錄接收數(shù)據(jù)的名字(不是路徑哦)
    self.toPath = [response.suggestedFilename cacheDir];
    NSLog(@"%@",self.toPath);
    [mgr createFileAtPath:self.toPath contents:nil attributes:nil];


}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    // 數(shù)據(jù)流的模式,允許拼接罚斗。
    self.stream = [NSOutputStream outputStreamToFileAtPath:self.toPath append:YES];
    // 只有打開數(shù)據(jù)流,才能進行數(shù)據(jù)的拼接
    [self.stream open];
    
    // 數(shù)據(jù)流徙鱼,接收byte數(shù)據(jù) 長度為:最大接收數(shù)據(jù)的長度,如果不這么設(shè)置的話针姿,接收到的數(shù)據(jù)是有問題的袱吆。
    [self.stream write:data.bytes maxLength:data.length];
    // 設(shè)置數(shù)據(jù)長度
    self.currentLength += data.length;  
    self.progressView.progress = (float)1.0*self.currentLength / self.totalLength;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
     NSLog(@"%s",__func__);
// 必須和句柄一樣 .關(guān)閉
    [self.stream close];
}

第四種:NSURLSession
NSURLSessionTask--->普通下載

  /**
     *  參數(shù)1、配置搓幌,一般為默認配置
     *  參數(shù)2杆故、設(shè)置代理,誰成為它的代理 遵守協(xié)議[代理方法中接受數(shù)據(jù)]
     *  參數(shù)3、代理回調(diào)信息 到那個隊列(線程)[一般為主隊列]
     *  注意 子協(xié)議 繼承與父協(xié)議溉愁, 遵守子協(xié)議处铛, 就是遵守父協(xié)議
     */
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"];
// 創(chuàng)建人物,用于下載數(shù)據(jù)   
 NSURLSessionTask *task = [session dataTaskWithURL:url];
    [task resume];

代理方法對接收到的數(shù)據(jù)進行處理,遵守的協(xié)議是:NSURLSessionDataDelegate

#pragma mark - NSURLSessionDataDelegate
// 接收到響應(yīng)
/**
 *  接收到響應(yīng)時調(diào)用
 *
 *  @param session
 *  @param dataTask          觸發(fā)事件的任務(wù)
 *  @param response          響應(yīng)頭
 *  @param completionHandler 回調(diào)(告訴我們?nèi)绾谓邮眨ㄊ欠瘢?shù)據(jù))
 *    NSURLSessionResponseCancel = 0,   默認情況 為 取消 接收數(shù)據(jù)
 *    NSURLSessionResponseAllow = 1,    總是允許接收數(shù)據(jù)
 *    NSURLSessionResponseBecomeDownload = 2  成為下載時 才接收數(shù)據(jù)
 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    //告訴系統(tǒng),我們始終接收數(shù)據(jù) 
completionHandler(NSURLSessionResponseAllow);
  // 創(chuàng)建文件,用來接收數(shù)據(jù)
    NSFileManager *mgr = [NSFileManager defaultManager];
    
    NSString *toPath = [response.suggestedFilename cacheDir];
    self.toPath = toPath;
    NSLog(@"%@",toPath);
    [mgr createFileAtPath:toPath contents:nil attributes:nil];
  
}
// 輸出流懶加載
- (NSOutputStream *)stream
{
    if (!_stream) {
        _stream = [NSOutputStream outputStreamToFileAtPath:self.toPath append:YES];
    }
    return _stream;

}
// 接收到數(shù)據(jù)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
//這里我們直接用數(shù)據(jù)輸出流來接受數(shù)據(jù). 為了避免多次創(chuàng)建輸出流,我們利用懶加載,對輸出流進行處理
//    NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:self.toPath append:YES];
//    
//    self.stream = stream;
    // 記住要打開數(shù)據(jù)流
    [self.stream open];
    [self.stream write:data.bytes maxLength:data.length];
}
// 接收完成
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
// 關(guān)閉輸出流
    [self.stream close];
    self.stream = nil;
    // 接收完成 給空間賦值饲趋。
    NSData *data = [NSData dataWithContentsOfFile:self.toPath];
    UIImage *image = [UIImage imageWithData:data];
    self.imageView.image = image;
    
    NSLog(@"%s",__func__);
}

NSURLSessionTask可以實現(xiàn)更為簡單的斷點下載,蘋果為我們提供了兩個方法: 1.暫停suspend,2.開始resume
, 但調(diào)用suspend時, 任務(wù)會被暫停.當調(diào)用resume是,任務(wù)會從暫停的地方再次下載. 省去了NSURLConnection中請求頭記錄傳入數(shù)據(jù)的麻煩.

在NSURLSession中提供了專門用于包裝下載任務(wù)的類NSURLSessionDownloadTask----->普通下載

// 設(shè)置session會話,專門用來處理任務(wù)的執(zhí)行
 NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];

    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_16.mp4"];
// 利用會話,創(chuàng)建下載任務(wù) 
    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url];
    // 對于不是block創(chuàng)建的任務(wù),不會立即執(zhí)行, 要調(diào)用resume
    [task resume];

代理方法的實現(xiàn)遵守的協(xié)議是:NSURLSessionDownloadDelegate----代理方法中會把接收完成的數(shù)據(jù),存放在臨時文件中, 我們需要將文件調(diào)出來,放在我們希望的位置location

#pragma mark - NSURLSessionDownloadDelegate
/**
 *  開始寫入數(shù)據(jù)
 *
 *  @param session
 *  @param downloadTask              當前下載任務(wù)
 *  @param bytesWritten              當前這次寫入數(shù)據(jù)的大小
 *  @param totalBytesWritten         已經(jīng)寫入數(shù)據(jù)的大小
 *  @param totalBytesExpectedToWrite 預(yù)計寫入數(shù)據(jù)的總大小
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    self.progressView.progress = (float)1.0*totalBytesWritten / totalBytesExpectedToWrite ;
}
/**
 *  恢復(fù)和暫停
 *
 *  @param session
 *  @param downloadTask       當前下載任務(wù)
 *  @param fileOffset         恢復(fù)之后又從文件的什么地方開始進行寫入
 *  @param expectedTotalBytes 文件剩余總大小
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
 NSLog(@"%s",__func__);
}
/**
 *  下載完成時
 *
 *  @param session
 *  @param downloadTask 當前下載任務(wù) (屬性中有響應(yīng)頭)
 *  @param location     下載的位置
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    NSURLResponse *response = downloadTask.response;
    
    NSString *toPath = [response.suggestedFilename cacheDir];
    NSLog(@"%@",toPath);
    
    NSFileManager *mgr = [NSFileManager defaultManager];
    
    // 將保存的臨時文件,轉(zhuǎn)移到撤蟆,指定目錄
    [mgr moveItemAtURL:location toURL:[NSURL fileURLWithPath:toPath] error:nil];

}

// 完成任務(wù)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
 NSLog(@"%s",__func__);

}

NSURLSessionDownLoadTask--->斷點下載的應(yīng)用
NSURLSessionDownloadTask 為我們提供了新的斷點下載的方法: cancelByProducingResumeData:void(^)(NSData *resumeData) 用于取消任務(wù)時,保存復(fù)原的數(shù)據(jù),并記錄當重新啟動任務(wù)時,需要從何處繼續(xù)加載 ---- 主要是用于暫停時數(shù)據(jù)的保留
dowmloadTaskWithResumeData:
必須在調(diào)用 resume才能開始任務(wù)
恢復(fù)下載時調(diào)用, 并且重新啟動下載任務(wù)時 , 下載任務(wù)的起點已經(jīng)發(fā)生了變化, 由傳入的數(shù)據(jù) 開始啟動任務(wù)---用于啟動時,設(shè)置數(shù)據(jù)開始下載的起點

對于斷點下載的優(yōu)化------>對于每次啟動程序, 我們并不需要重新下載, 只需要在本地沙盒中存儲的數(shù)據(jù)--->繼續(xù)下載就可以了

  1. 需要完成的就是, 發(fā)送請求的時候, 先讀取本地數(shù)據(jù), 并計算本地數(shù)據(jù)的大小, 然后在請求頭設(shè)置時,加入請求數(shù)據(jù)開始范圍
// 設(shè)置會話,并設(shè)置會話的代理, 以及回調(diào)隊列
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
   
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_16.mp4"];
   
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    
   // 通過本地文件得到奕塑,下載了多少數(shù)據(jù)。 一旦重新啟動任務(wù)家肯,會從多少數(shù)據(jù)開始龄砰。 但是一旦重啟,數(shù)據(jù)的響應(yīng)頭 中期望數(shù)據(jù)長度讨衣,將會改變换棚,改變量為已將下好的長度,與總長度只差
    self.fileSize = [self fileSizeWith:Name];
    NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.fileSize];
  // 設(shè)置請求頭內(nèi)容
    [request setValue:range forHTTPHeaderField:@"Range"];
   // 創(chuàng)建下載 
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
    self.task = task;
    [self.task resume];

2.創(chuàng)建方法,專門用于計算文件內(nèi)容的大小

// 根據(jù)傳入的文件名,來計算文件的大小
- (NSInteger)fileSizeWith:(NSString *)str;
{
    NSFileManager *mgr = [NSFileManager defaultManager];
    
    NSString *toPath = [str cacheDir];
// 文件中的相關(guān)參數(shù)
    NSDictionary *dict = [mgr attributesOfItemAtPath:toPath error:nil];
    
    return  [dict[NSFileSize] integerValue];
}

對于多次啟動相同下載任務(wù)的優(yōu)化(懶加載任務(wù))

#define Name  @"minion_16.mp4"
@implementation ViewController
- (NSURLSessionDataTask *)task
{
    if (!_task) {
        NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
        
        NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_16.mp4"];
        
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
     
        self.fileSize = [self fileSizeWith:Name];
        NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.fileSize];
        
        [request setValue:range forHTTPHeaderField:@"Range"];
        
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
        self.task = task;

    }
    return _task;
}

Tools 工具類:

為了能更方便的進行下載內(nèi)容存儲,所以我們給下載文件到沙盒路徑提供更為簡單的方法, 外界調(diào)用最方便的方法.
對NSString添加分類,創(chuàng)建實現(xiàn)方法

// 傳入字符串,直接在沙盒Cache中生成路徑
- (instancetype)cacheDir
{
    NSString *cache = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
   return [cache stringByAppendingPathComponent:[self lastPathComponent]];
}
// 傳入字符串,直接在沙盒Document中生成路徑
- (instancetype)docDir
{

    NSString *doc = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES).firstObject;
    return [doc stringByAppendingPathComponent:[self lastPathComponent]];

}
// 傳入字符串,直接在沙盒Temp中生成路徑
- (instancetype)temDir
{

    NSString *tem = NSTemporaryDirectory();
    return [tem stringByAppendingPathComponent:[self lastPathComponent]];

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末反镇,一起剝皮案震驚了整個濱河市固蚤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌歹茶,老刑警劉巖夕玩,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異惊豺,居然都是意外死亡燎孟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門尸昧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揩页,“玉大人,你說我怎么就攤上這事彻磁“澹” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵衷蜓,是天一觀的道長累提。 經(jīng)常有香客問我,道長磁浇,這世上最難降的妖魔是什么斋陪? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮置吓,結(jié)果婚禮上无虚,老公的妹妹穿的比我還像新娘。我一直安慰自己衍锚,他們只是感情好友题,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著戴质,像睡著了一般度宦。 火紅的嫁衣襯著肌膚如雪踢匣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天戈抄,我揣著相機與錄音离唬,去河邊找鬼。 笑死划鸽,一個胖子當著我的面吹牛输莺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播裸诽,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼嫂用,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了丈冬?” 一聲冷哼從身側(cè)響起尸折,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎殷蛇,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體橄浓,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡粒梦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了荸实。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匀们。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖准给,靈堂內(nèi)的尸體忽然破棺而出泄朴,到底是詐尸還是另有隱情,我是刑警寧澤露氮,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布祖灰,位于F島的核電站,受9級特大地震影響畔规,放射性物質(zhì)發(fā)生泄漏局扶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一叁扫、第九天 我趴在偏房一處隱蔽的房頂上張望三妈。 院中可真熱鬧,春花似錦莫绣、人聲如沸畴蒲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽模燥。三九已至咖祭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涧窒,已是汗流浹背心肪。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纠吴,地道東北人硬鞍。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像戴已,于是被迫代替她去往敵國和親固该。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容