zoukankan      html  css  js  c++  java
  • iOS 文件下载及断点续传

      ios的下载我们可以使用的方法有:NSData、NSURLConnection、NSURLSession还有第三方框架AFNetworking和ASI

      利用NSData方法和NSURLConnection的异步方法仅适合下载较小的文件,因为NSData是把数据一口气下载,下载大文件那么手机的内存会一下子暴涨,大文件下载可以用NSURLConnection代理方法、NSURLSession代理方法、AFNetworking和ASI:

      NSData:

    // 在沙盒中得存储路径
        NSString *str = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        NSString *path = [str stringByAppendingPathComponent:@"test.exe"];
        
        NSLog(@"%@",str);
        
        // 下载地址
        NSURL *url = [NSURL URLWithString:@"http://68.duote.com.cn/fcengw.exe"];
        
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 数据写入到沙盒
        [data writeToFile:path atomically:YES];

      NSURLConnection异步方法:

    // 在沙盒中得存储路径
        NSString *str = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        NSString *path = [str stringByAppendingPathComponent:@"test.exe"];
        
        NSLog(@"%@",str);
        
        // 下载地址
        NSURL *url = [NSURL URLWithString:@"http://68.duote.com.cn/fcengw.exe"];
        
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        
        // 异步方法
        [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
            
            // 数据写入沙盒
            [data writeToFile:path atomically:YES];
        }];

      利用NSURLConnection的代理方法来实现下载(NSURLConnectionDataDelegate):

      这其中使用了响应头expectedContentLength来获取文件的总大小,利用了请求头的键“Range”来设置暂停后下一次开始时的位置,利用了NSFileManager和NSFileHandle来设置分段存储下载的数据:

    #pragma mark - 属性
    // 文件句柄对象
    @property (nonatomic,strong) NSFileHandle *fileHandle;
    
    // 文件总长度
    @property (nonatomic,assign) long long contentLength;
    
    // 当前文件长度
    @property (nonatomic,assign) long long currentLen;
    
    @property (nonatomic,strong) NSURLConnection *conn;
    
    #pragma mark - 点击事件
    // 按钮点击事件
    - (IBAction)startAndStop {
        
        // 设置按钮选中与否
        _btn.selected = !_btn.selected;
        
        // 实现断点续传
        if (_btn.selected){ // 按钮选中
            
            // 下载地址
            NSString *str = @"http://51.duote.org/haozip_duote.exe";
            
            NSURL *url = [NSURL URLWithString:str];
            
            NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
            
            // 设置请求头数据
            NSString *range = [NSString stringWithFormat:@"bytes=%lld-",self.currentLen];
            [request setValue:range forHTTPHeaderField:@"Range"];
            
            self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
            
        }else{
            
            // 取消下载(暂停)
            [self.conn cancel];
            self.conn = nil;
        }
    }
    
    #pragma mark - NSURLConnectionDataDelegate代理
    // 出错
    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
        
        NSLog(@"出错 - %@",error.localizedDescription);
    }
    
    // 接收到响应
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
        if (self.currentLen) return;
        
        NSString *cacheStr = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        NSString *path = [cacheStr stringByAppendingPathComponent:@"test.exe"];
        
        // 创建一个空的文件
        NSFileManager *mgr = [NSFileManager defaultManager];
        [mgr createFileAtPath:path contents:nil attributes:nil];
        
      // 句柄对象实例化 self.fileHandle
    = [NSFileHandle fileHandleForWritingAtPath:path]; // 文件总大小(响应头取得) NSHTTPURLResponse *resp = (NSHTTPURLResponse *)response; self.contentLength = resp.expectedContentLength; } // 接收数据 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 找到文件的末尾 [self.fileHandle seekToEndOfFile]; // 在末尾写入数据 [self.fileHandle writeData:data]; // 当前已下载长度 self.currentLen += data.length; // 下载百分比:当前已下载大小 / 文件总大小 self.progressView.progress = (double)self.currentLen / self.contentLength; NSLog(@"%f",self.progressView.progress); } // 接收完成 - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"接收完成"); // 关闭 [self.fileHandle closeFile]; self.fileHandle = nil; self.currentLen = 0; self.contentLength = 0; }

      利用NSURLSession的代理方法:

      NSURLSession是ios7后才有的方法,苹果官方旨在替代NSURLConnection

      NSURLSessionDataTask:解决一般的GETPOST的请求方法

      NSURLSessionDownloadTask:解决文件下载

      NSURLSessionUploadTask:文件上传

      A、NSURLSessionDataTask的操作:

    // 下载地址
        NSURL *url = [NSURL URLWithString:@"http://68.duote.com.cn/fcengw.exe"];
        
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            
                // 这里进行json或XML解析
        }];
        
        // 3.开始任务
        [task resume];

      B、NSURLSessionDownloadTask:不能看到下载进度

    // session对象
        NSURLSession *session = [NSURLSession sharedSession];
        
        // 下载地址
        NSString *str = @"http://51.duote.org/haozip_duote.exe";
        
        // 创建一个下载task
        NSURL *url = [NSURL URLWithString:str];
        NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
            
            //////  因为任务下载是存在沙盒的temp文件中,并且下载完成后会被立即清除,所以这里将下载的文件剪切到caches中
            
            // location : 临时文件的路径(下载好的文件)
            
            NSString *cacheStr = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
            // response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致
            NSString *path = [cacheStr stringByAppendingPathComponent:response.suggestedFilename];
            
            // 将临时文件剪切或者复制Caches文件夹
            NSFileManager *mgr = [NSFileManager defaultManager];
            
            // AtPath : 剪切前的文件路径
            // ToPath : 剪切后的文件路径
            [mgr moveItemAtPath:location.path toPath:path error:nil];
        }];
        
        // 开始任务
        [task resume];

      C:NSURLSessionDownloadDelegate的实现:

    NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration];
    
    // session对象
    NSURLSession *session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    
    // 下载task
    NSURL *url = [NSURL URLWithString:@"http://51.duote.org/haozip_duote.exe"];
    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url]; // 这里不要用block,如果用了block,那么方法会优先调用block,而不是代理方法
    // 开始任务
    [task resume];
    
    #pragma mark - NSURLSessionDownloadDelegate代理
    // 下载完毕后调用
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
    {
        
        NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        // response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致
        
        NSString *file = [caches stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
        
        // 将临时文件剪切或者复制Caches文件夹
        NSFileManager *mgr = [NSFileManager defaultManager];
        
        // AtPath : 剪切前的文件路径
        // ToPath : 剪切后的文件路径
        [mgr moveItemAtPath:location.path toPath:file error:nil];
    }
    
    
    // 恢复下载时调用
    
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
    {
    
    }
    
    
    // 每当下载一部分就会调用,可能会被调用多次
    // bytesWritten              这次下载了多少
    // totalBytesWritten         当前总共下载了多少到沙盒
    // totalBytesExpectedToWrite 文件的总大小
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
    {
        double progress = (double)totalBytesWritten / totalBytesExpectedToWrite;
        NSLog(@"下载进度---%f", progress);
    }

       D、NSURLSession实现断点续传

    // .m文件
    @property (nonatomic, strong) NSURLSessionDownloadTask *task;
    @property (nonatomic, strong) NSData *resumeData;
    @property (nonatomic, strong) NSURLSession *session;
    
    
    - (NSURLSession *)session
    {
        if (!_session) {
            // 获得session
            NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration];
            self.session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]];
        }
        return _session;
    }
    
    // 按钮点击事件
    - (IBAction)download:(UIButton *)sender {
        // 按钮状态取反
        sender.selected = !sender.isSelected;
        
        if (self.task == nil) { // 开始(继续)下载
            if (self.resumeData) { // 恢复
                [self resume];
            } else { // 开始
                [self start];
            }
        } else { // 暂停
            [self pause];
        }
    }
    
    // 开始下载
    - (void)start
    {
        // 创建一个下载任务
        NSURL *url = [NSURL URLWithString:@"http://xxx.com/mmg/cc.exe"];
        self.task = [self.session downloadTaskWithURL:url];
        
        // 开始任务
        [self.task resume];
    }
    
    // 恢复下载
    - (void)resume
    {
        // 传入上次暂停下载返回的数据,就可以恢复下载
        self.task = [self.session downloadTaskWithResumeData:self.resumeData];
        
        // 开始任务
        [self.task resume];
        
        // 清空
        self.resumeData = nil;
    }
    
    // 暂停下载
    - (void)pause
    {
        __weak typeof(self) vc = self;
        [self.task cancelByProducingResumeData:^(NSData *resumeData) {
            //  resumeData : 包含了继续下载的开始位置下载的url
            vc.resumeData = resumeData;
            vc.task = nil;
        }];
    }
    
    #pragma mark - NSURLSessionDownloadDelegate 下载代理方法
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {
        NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        // response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致
        NSString *file = [caches stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
        
        // 将临时文件剪切或者复制Caches文件夹
        NSFileManager *mgr = [NSFileManager defaultManager];
        
        // AtPath : 剪切前的文件路径
        // ToPath : 剪切后的文件路径
        [mgr moveItemAtPath:location.path toPath:file error:nil];
    }
    
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
          didWriteData:(int64_t)bytesWritten
     totalBytesWritten:(int64_t)totalBytesWritten
    totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
    {
        NSLog(@"获得下载进度--%@", [NSThread currentThread]);
        // 获得下载进度
        self.progressView.progress = (double)totalBytesWritten / totalBytesExpectedToWrite;
    }
    
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
     didResumeAtOffset:(int64_t)fileOffset
    expectedTotalBytes:(int64_t)expectedTotalBytes
    {
        
    }
    欢迎加QQ群交流: iOS: 279096195 React Native: 482205185
  • 相关阅读:
    bootmgr is missing
    【转】ahci和IDE的区别
    win10系统故障代码:0xc000014c
    c++小数点后舍入
    关于类里再声明自身类实例的思考
    Java集合
    Python图片转字符画
    102. Binary Tree Level Order Traversal
    1041. Robot Bounded In Circle
    144. Binary Tree Preorder Traversal
  • 原文地址:https://www.cnblogs.com/GeekStar/p/4409714.html
Copyright © 2011-2022 走看看