如果在網(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];
}];
}
}
}
}
}
}