使用NSURLSession實(shí)現(xiàn)iOS大文件分塊下載

如果在網(wǎng)速一定的情境下,大文件(目前指的是100M以上的文件)的下載對用戶來說是一段不短的時(shí)間蒜撮,用戶體驗(yàn)不是很好跪呈。
分塊下載文件實(shí)現(xiàn)原理
如果要實(shí)現(xiàn)文件的分段下載榔至,我們首先需要知道要下載的文件的大小梅肤,這里需要向服務(wù)器發(fā)送HTTP請求琉用,請求方法為HEAD,這樣服務(wù)器只會給客戶端返回response的包頭信息邑时,不會發(fā)送數(shù)據(jù)信息,然后我們通過包頭信息中的Content-Length字段可以得到要下載的文件的總長度黍氮。

- (void)getFileTotalLengthWithURL:(NSString *)url
                       completion:(void(^)(NSInteger length))completion{
    NSURL *URL = [NSURL URLWithString:url];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
    request.HTTPMethod = @"HEAD";
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
            NSHTTPURLResponse *tmpResponse = (NSHTTPURLResponse *)response;
            NSLog(@"allHeaderFields:%@", tmpResponse.allHeaderFields);
        }
        NSInteger fileTotalLength = response.expectedContentLength;
        completion(fileTotalLength);
    }];
    [dataTask resume];
}

獲取到要下載的文件的總長度之后沫浆,在本地沙盒中創(chuàng)建一個同樣大小的文件

- (void)multiDownloadWithFileLength:(NSInteger)fileLength url:(NSURL *)url{
    _wholeFileLength = fileLength;

    NSString *filePath = [self filePathWithFileName:url.lastPathComponent];
    NSFileManager *fm = [NSFileManager defaultManager];
    if ([fm fileExistsAtPath:filePath]) {
        [fm removeItemAtPath:filePath error:nil];
    }
    [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];

    _filePath = filePath;
    _fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
    [_fileHandle truncateFileAtOffset:fileLength];
    
    NSBlockOperation *addOperationOP = [NSBlockOperation blockOperationWithBlock:^{
        while (_completedLength < fileLength) {
            long long startSize = _completedLength;
            long long endSize = startSize+blockSize;
            
            if (endSize > fileLength) {
                endSize = fileLength - 1;
                _completedLength = fileLength;
            } else {
                _completedLength += blockSize;
            }
            
            //一個operation對應(yīng)一個downloadTask
            
            NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                
                NSString *range=[NSString stringWithFormat:@"bytes=%lld-%lld", startSize, endSize];
                NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
                [request setValue:range forHTTPHeaderField:@"Range"];
                NSLog(@"requestHeader:%@", request.allHTTPHeaderFields);
                NSURLSessionDownloadTask *task = [self.session downloadTaskWithRequest:request];
                
                [task resume];
                
            }];
            [_queue addOperation:operation];
        }
    }];
    [_queue addOperation:addOperationOP];
    
}

每個請求塊的大小

#define blockSize 1024*1024

新建一個下載隊(duì)列郁油,循環(huán)發(fā)送請求,在請求頭中設(shè)置Range字段

NSOperationQueue *queue = [NSOperationQueue currentQueue];
    NSBlockOperation *addOperationOP = [NSBlockOperation blockOperationWithBlock:^{
        while (_completedLength < fileLength) {
            long long startSize = _completedLength;
            long long endSize = startSize+blockSize;
            
            if (endSize > fileLength) {
                endSize = fileLength - 1;
                _completedLength = fileLength;
            } else {
                _completedLength += blockSize;
            }
            
            //一個operation對應(yīng)一個downloadTask
            
            NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
                
                NSString *range=[NSString stringWithFormat:@"bytes=%lld-%lld", startSize, endSize];
                NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
                [request setValue:range forHTTPHeaderField:@"Range"];
                NSLog(@"requestHeader:%@", request.allHTTPHeaderFields);
                NSURLSessionDownloadTask *task = [self.session downloadTaskWithRequest:request];
                
                [task resume];
                
            }];
            [queue addOperation:operation];
        }
    }];
    [queue addOperation:addOperationOP];

在代理方法中將獲取到的數(shù)據(jù)寫到已經(jīng)創(chuàng)建好的空文件的對應(yīng)的位置中

 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
    
    DLQData *tmpReceivedData = [[DLQData alloc] init];
    NSInteger startSize = 0;
    NSInteger endSize = 0;
    
    if ([downloadTask.response isKindOfClass:[NSHTTPURLResponse class]]) {
        NSHTTPURLResponse *tmpResponse = (NSHTTPURLResponse *)downloadTask.response;
        NSDictionary *dic = tmpResponse.allHeaderFields;
        NSLog(@"diiiiiic: %@", dic[@"Content-Range"]);
        NSString *fileRange = dic[@"Content-Range"];
        fileRange = [fileRange stringByReplacingOccurrencesOfString:@"bytes" withString:@""];
        fileRange = [fileRange stringByReplacingOccurrencesOfString:@" " withString:@""];
        NSArray *aTmp1 = [fileRange componentsSeparatedByString:@"/"];
        NSArray *aTmp2 = @[];
        if (aTmp1.count) {
            NSString *tmpStr = aTmp1[0];
            aTmp2 = [tmpStr componentsSeparatedByString:@"-"];
            if (aTmp1.count >= 2) {
                NSString *startSizeStr = aTmp2[0];
                NSString *endSizeStr = aTmp2[1];
                startSize = startSizeStr.integerValue;
                endSize = endSizeStr.integerValue;
        
                tmpReceivedData.data = [NSData dataWithContentsOfURL:location];
                tmpReceivedData.startSize = startSize;
                tmpReceivedData.endSize = endSize;
                
                [_fileHandle seekToFileOffset:tmpReceivedData.startSize];
                [_fileHandle writeData:tmpReceivedData.data];
                [_fileData appendData:tmpReceivedData.data];
                
                double progress = _fileData.length/_wholeFileLength;
                progress = progress >= 1 ? 1 : progress;
                if (progress == 1) {
                    NSLog(@"分段下載完成");
                    NSLog(@"downloadProgress:%f", progress);
                    
                    [_operationQueue cancelAllOperations];
                    _operationQueue = nil;
                    
                    if ([self.delegate respondsToSelector:@selector(multiDownloadDidFinished:)] && [self.delegate respondsToSelector:@selector(multiDownloadProgress:)]) {
                        NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
                        [mainQueue addOperationWithBlock:^{
                            [self.delegate multiDownloadProgress:progress];
                            [self.delegate multiDownloadDidFinished:_filePath];

                        }];
                    }
                }else{
                    if ([self.delegate respondsToSelector:@selector(multiDownloadProgress:)]) {
                        NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
                        [mainQueue addOperationWithBlock:^{
                            [self.delegate multiDownloadProgress:progress];
                        }];
                    }
                }
            }
        }
    }
}

點(diǎn)我看Demo

參考文章
iOS開發(fā)網(wǎng)絡(luò)篇—多線程斷點(diǎn)下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市盒件,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恩沽,老刑警劉巖罗心,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渤闷,死亡現(xiàn)場離奇詭異飒箭,居然都是意外死亡蜒灰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門凸椿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來脑漫,“玉大人咙崎,你說我怎么就攤上這事⌒鸱玻” “怎么了?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵跛璧,是天一觀的道長追城。 經(jīng)常有香客問我燥撞,道長迷帜,這世上最難降的妖魔是什么戏锹? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任锦针,我火速辦了婚禮置蜀,結(jié)果婚禮上盯荤,老公的妹妹穿的比我還像新娘秋秤。我一直安慰自己灼卢,他們只是感情好芥玉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布灿巧。 她就那樣靜靜地躺著抠藕,像睡著了一般盾似。 火紅的嫁衣襯著肌膚如雪零院。 梳的紋絲不亂的頭發(fā)上村刨,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天告抄,我揣著相機(jī)與錄音,去河邊找鬼嵌牺。 笑死打洼,一個胖子當(dāng)著我的面吹牛龄糊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播募疮,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼炫惩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了阿浓?” 一聲冷哼從身側(cè)響起他嚷,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搔扁,沒想到半個月后爸舒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涂炎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年网梢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拣宰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出潜圃,到底是詐尸還是另有隱情堵第,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響伪朽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一需了、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧住拭,春花似錦、人聲如沸谱煤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豁辉。三九已至气破,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像甲喝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子诵冒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評論 2 354

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)铁蹈,斷路器娩梨,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 點(diǎn)擊查看原文 Web SDK 開發(fā)手冊 SDK 概述 網(wǎng)易云信 SDK 為 Web 應(yīng)用提供一個完善的 IM 系統(tǒng)...
    layjoy閱讀 13,760評論 0 15
  • iOS開發(fā)系列--網(wǎng)絡(luò)開發(fā) 概覽 大部分應(yīng)用程序都或多或少會牽扯到網(wǎng)絡(luò)開發(fā)企巢,例如說新浪微博、微信等神帅,這些應(yīng)用本身可...
    lichengjin閱讀 3,658評論 2 7
  • 簡介 用簡單的話來定義tcpdump讨永,就是:dump the traffic on a network锻霎,根據(jù)使用者...
    保川閱讀 5,956評論 1 13
  • 大約半個月前昂勒,我媽在干活的時(shí)候小腿骨折了,除了骨折還有幾處裂紋。幾年前她在干農(nóng)活的時(shí)候腳踝裂了,后來打了石膏修養(yǎng)了...
    輕燕舒展閱讀 260評論 0 0