zoukankan      html  css  js  c++  java
  • iOS开发-大文件下载与断点下载思路

    • 大文件下载
      方案一:利用NSURLConnection和它的代理方法,及NSFileHandle(iOS9后不建议使用)
      相关变量:
      @property (nonatomic,strong) NSFileHandle *writeHandle;
      @property (nonatomic,assign) long long totalLength;
      1>发送请求
      // 创建一个请求
          NSURL *url = [NSURL URLWithString:@""];
          NSURLRequest *request = [NSURLRequest requestWithURL:url];
          // 使用NSURLConnection发起一个异步请求
          [NSURLConnection connectionWithRequest:request delegate:self];

      2>在代理方法中处理服务器返回的数据

      /** 在接收到服务器的响应时调用下面这个代理方法
          1.创建一个空文件
          2.用一个句柄对象关联这个空文件,目的是方便在空文件后面写入数据
      */
      - (void)connection:(NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response
      {
          // 创建文件路径
          NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
          NSString *filePath = [caches stringByAppendingPathComponent:@"videos.zip"];
          
          // 创建一个空的文件到沙盒中
          NSFileManager *mgr = [NSFileManager defaultManager];
          [mgr createFileAtPath:filePath contents:nil attributes:nil];
          
          // 创建一个用来写数据的文件句柄
          self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
          
          // 获得文件的总大小
          self.totalLength = response.expectedContentLength;
      }
      
      /** 在接收到服务器返回的文件数据时调用下面这个代理方法
          利用句柄对象往文件的最后面追加数据
       */
      - (void)connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)data
      {
          // 移动到文件的最后面
          [self.writeHandle seekToEndOfFile];
          
          // 将数据写入沙盒
          [self.writeHandle writeData:data];
      }
      
      /**
          在所有数据接收完毕时,关闭句柄对象
       */
      - (void)connectionDidFinishLoading:(NSURLConnection *)connection
      {
          // 关闭文件并清空
          [self.writeHandle closeFile];
          self.writeHandle = nil;
      }

      方案二:使用NSURLSession的NSURLSessionDownloadTask和NSFileManager

      NSURLSession *session = [NSURLSession sharedSession];
          NSURL *url = [NSURL URLWithString:@""];
          // 可以用来下载大文件,数据将会存在沙盒里的tmp文件夹
          NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
              // location :临时文件存放的路径(下载好的文件)
              
              // 创建存储文件路径
              NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
              // response.suggestedFilename:建议使用的文件名,一般跟服务器端的文件名一致
              NSString *file = [caches stringByAppendingPathComponent:response.suggestedFilename];
              
              /**将临时文件剪切或者复制到Caches文件夹
               AtPath :剪切前的文件路径
               toPath :剪切后的文件路径
               */
              NSFileManager *mgr = [NSFileManager defaultManager];
              [mgr moveItemAtPath:location.path toPath:file error:nil];
          }];
          [task resume];

      方案三:使用NSURLSessionDownloadDelegate的代理方法和NSFileManger

      - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
      {
          // 创建一个下载任务并设置代理
          NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration];
          NSURLSession *session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]];
          
          NSURL *url = [NSURL URLWithString:@""];
          NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url];
          [task resume];
      }
      
      #pragma mark - 
      /**
          下载完毕后调用
          参数:lication 临时文件的路径(下载好的文件)
       */
      - (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文件夹
           AtPath :剪切前的文件路径
           toPath :剪切后的文件路径
           */
          NSFileManager *mgr = [NSFileManager defaultManager];
          [mgr moveItemAtPath:location.path toPath:file error:nil];
      }
      
      /**
          每当下载完一部分时就会调用(可能会被调用多次)
          参数:
              bytesWritten 这次调用下载了多少
              totalBytesWritten 累计写了多少长度到沙盒中了
              totalBytesExpectedToWrite 文件总大小
       */
      - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
            didWriteData:(int64_t)bytesWritten
       totalBytesWritten:(int64_t)totalBytesWritten
      totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
          // 这里可以做些显示进度等操作
      }
      
      /**
          恢复下载时使用
       */
      - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
       didResumeAtOffset:(int64_t)fileOffset
      expectedTotalBytes:(int64_t)expectedTotalBytes
      {
          // 用于断点续传
      }
    • 断点下载
      方案一:
      1>在方案一的基础上新增两个变量和按扭
      @property (nonatomic,assign) long long currentLength;
      @property (nonatomic,strong) NSURLConnection *conn;

      2>在接收到服务器返回数据的代理方法中添加如下代码

          // 记录断点,累计文件长度
          self.currentLength += data.length;

      3>点击按钮开始(继续)或暂停下载

      - (IBAction)download:(UIButton *)sender {
          
          sender.selected = !sender.isSelected;
          
          if (sender.selected) { // 继续(开始)下载
              NSURL *url = [NSURL URLWithString:@""];
              // ****关键点是使用NSMutableURLRequest,设置请求头Range
              NSMutableURLRequest *mRequest = [NSMutableURLRequest requestWithURL:url];
              
              NSString *range = [NSString stringWithFormat:@"bytes=%lld-",self.currentLength];
              [mRequest setValue:range forHTTPHeaderField:@"Range"];
              
              // 下载
              self.conn = [NSURLConnection connectionWithRequest:mRequest delegate:self];
          }else{
              [self.conn cancel];
              self.conn = nil;
          }
      }

      4>在接受到服务器响应执行的代理方法中第一行添加下面代码,防止重复创建空文件

       if (self.currentLength)  return;

      方案二:使用NSURLSessionDownloadDelegate的代理方法
      所需变量

      @property (nonatomic,strong) NSURLSession *session;
      @property (nonatomic,strong) NSData *resumeData; //包含了继续下载的开始位置和下载的url
      @property (nonatomic,strong) NSURLSessionDownloadTask *task;

      方法

      // 懒加载session
      - (NSURLSession *)session
      {
          if (!_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:@""];
          self.task = [self.session downloadTaskWithURL:url];
          [self.task resume];
      }
      
      // 暂停
      - (void)pause{
          __weak typeof(self) vc = self;
          [self.task cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
              //resumeData : 包含了继续下载的开始位置和下载的url
              vc.resumeData = resumeData;
              vc.task = nil;
          }];
      }
      
      // 恢复
      - (void)resume{
          // 传入上次暂停下载返回的数据,就可以回复下载
          self.task = [self.session downloadTaskWithResumeData:self.resumeData];
          // 开始任务
          [self.task resume];
          // 清空
          self.resumeData = nil;
      }
      
      #pragma mark - NSURLSessionDownloadDelegate
      /**
          下载完毕后调用
          参数:lication 临时文件的路径(下载好的文件)
       */
      - (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文件夹
           AtPath :剪切前的文件路径
           toPath :剪切后的文件路径
           */
          NSFileManager *mgr = [NSFileManager defaultManager];
          [mgr moveItemAtPath:location.path toPath:file error:nil];
      }
      
      /**
          每当下载完一部分时就会调用(可能会被调用多次)
          参数:
              bytesWritten 这次调用下载了多少
              totalBytesWritten 累计写了多少长度到沙盒中了
              totalBytesExpectedToWrite 文件总大小
       */
      - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
            didWriteData:(int64_t)bytesWritten
       totalBytesWritten:(int64_t)totalBytesWritten
      totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
          // 这里可以做些显示进度等操作
      }
      
      /**
          恢复下载时使用
       */
      - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
       didResumeAtOffset:(int64_t)fileOffset
      expectedTotalBytes:(int64_t)expectedTotalBytes
      {
      }



  • 相关阅读:
    Unix Domain Socket(基于 Linux-2.4.0已更新)
    Eclipse常用设置及部分常用快捷键个人总结(MyEclipse通用)
    使用Docker安装ElasticSearch 以及我遇到的问题
    使用Docker安装MySQL
    es 索引自动删除
    k8s部署zk集群
    k8s-基于Canal来定义网络策略
    Markdown学习
    python ---- Linux压缩某个目录但要跳过指定文件
    WEB前端 ---- 学习第二天(表格、表单、css等)
  • 原文地址:https://www.cnblogs.com/jierism/p/6284304.html
Copyright © 2011-2022 走看看