zoukankan      html  css  js  c++  java
  • iOS网络-通过NSURLConnection文件下载,断点下载,输出流下载

    小文件下载
    NSURLConnection-delegate实现,可以监听到下载的进度,但是还是无法解决内存飙升的问题,所以仅限小文件下载
     1 @interface ViewController ()<NSURLConnectionDataDelegate>
     2 @property (weak, nonatomic) IBOutlet UIProgressView *progressView;
     3 /** 接收数据的data */
     4 @property (nonatomic, strong) NSMutableData *fileData; 6 /*文件名称 */
     7 @property (nonatomic, strong) NSString *fileName;
     8 @property (nonatomic, assign) NSInteger totalSize;
     9 @end
    10 @implementation ViewController
    11 -(NSMutableData *)fileData
    12 {
    13     if (_fileData == nil) {
    14         _fileData = [NSMutableData data];
    15     }
    16     return _fileData;
    17 }
    18 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    19 {
    20     //1.确定URL
    21     NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
    22     //2.创建请求对象
    23     NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
    24     //3.设置代理,发送请求
    25     [NSURLConnection connectionWithRequest:request delegate:self];
    26 }
    27 #pragma mark - NSURLConnectionDataDelegate
    28 -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    29 {
    30     //suggestedFilename:服务器端推荐的名称,其实就是url的最后一个节点
    31     self.fileName = response.suggestedFilename;
    32     //expectedContentLength:获得文件总大小
    33 
    34     self.totalSize = response.expectedContentLength;
    35 }
    36 -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    37 {
    38     //拼接数据
    39     [self.fileData appendData:data];
    40     //计算文件的下载进度== 已经下载的大小/文件的总大小
    41     NSLog(@"%f",1.0 * self.fileData.length /self.totalSize);
    42     //进度UI43     self.progressView.progress = 1.0 * self.fileData.length /self.totalSize;
    44 }
    45 -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    46 {
    47 }
    48 -(void)connectionDidFinishLoading:(NSURLConnection *)connection
    49 {
    50     //写数据到磁盘
    51     NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    52     //拼接全路径
    53     NSString *fullPath = [caches stringByAppendingPathComponent:self.fileName];
    54     //写沙盒
    55     [self.fileData writeToFile:fullPath atomically:YES];
    56 }
    57 @end
    大文件下载 
    NSURLConnection-delegate实现, 实现思路: 边接收数据边写文件以解决内存越来越大的问题
    @interface ViewController ()<NSURLConnectionDataDelegate>
    @property (weak, nonatomic) IBOutlet UIProgressView *progressView;
    
    /*文件名称 */
    @property (nonatomic, strong) NSString *fileName;
    /*文件的总大小*/
    @property (nonatomic, assign) NSInteger totalSize;
    /*已经下载的文件大小*/
    @property (nonatomic, assign) NSInteger currentSize;
    /** 文件句柄 */
    @property (nonatomic, strong) NSFileHandle *handle;
    @end
    
    @implementation ViewController
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
         //1.确定URL
        NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
        //2.创建请求对象
        NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
        //3.设置代理,发送请求
        [NSURLConnection connectionWithRequest:request delegate:self];
    
    }
    #pragma mark - NSURLConnectionDataDelegate
    //接收到服务器的响应
    -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
        //1.获得caches目录
        NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
        //2.拼接全路径
        NSString *fullPath = [caches stringByAppendingPathComponent:response.suggestedFilename];
        //3.获得本次请求的文件数据大小(期望大小)
        self.totalSize = response.expectedContentLength;
        //4.新建一个空的文件
        /*
         第一个参数:文件的目录
         第二个参数:文件的内容
         第三个参数:文件的属性
         */
        [[NSFileManager defaultManager]createFileAtPath:fullPath contents:nil attributes:nil];
        //5.创建文件句柄
        NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:fullPath];
        self.handle = handle;
    }
    
    //接收到服务器返回的数据
    -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
        //1.移动文件句柄到文件的末尾
        [self.handle seekToEndOfFile];
        //2.写数据到磁盘
        [self.handle writeData:data];
        //3.计算文件的下载进度== 已经下载的大小/文件的总大小
        self.currentSize +=data.length;
        NSLog(@"%f",1.0 * self.currentSize /self.totalSize);
        //4.进度UI
        self.progressView.progress = 1.0 * self.currentSize /self.totalSize;
    }
    //请求完成
    -(void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        //释放文件句柄
        [self.handle closeFile];
        self.handle = nil;
    }
    @end
    大文件断点下载
        实现思路: 设置请求头NSFileHandle
         在下载文件的时候不再是整块的从头开始下载,而是看当前文件已经下载到哪个地方,然后从该地方接着往后面下载。可以通过在请求对象中设置请求头实现。
      1.设置请求头,在开始下载的时候设置
        //2.创建请求对象 可变的
           NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        //2.1 设置下载文件的某一部分
        // 只要设置HTTP请求头的Range属性, 就可以实现从指定位置开始下载
        /*
         表示头500个字节:Range: bytes=0-499
         表示第二个500字节:Range: bytes=500-999
         表示最后500个字节:Range: bytes=-500
         表示500字节以后的范围:Range: bytes=500-
         */
        NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentLength];
        [request setValue:range forHTTPHeaderField:@"Range"];

       2.注意点(下载进度并判断是否需要重新创建文件)

    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response接收到服务器的响应的方法中代码改动:

      //注意点:response.expectedContentLength获得是本次请求要下载的文件的大小(并非是完整的文件的大小)
    //
    文件的总大小 == 本次要下载的文件大小 + 已经下载的文件的大小 self.totalLength = response.expectedContentLength + self.currentLength; // 判断当前是否已经下载过,如果当前文件已经存在,那么直接返回,不然新下载的大小会覆盖原来文件,导致前面文件丢失 if (self.currentLength >0) { return; }
    大文件输出流下载
    使用输出流也可以实现和NSFileHandle相同的功能
        //1.创建一个数据输出流
        /*
         第一个参数:二进制的流数据要写入到哪里
         第二个参数:采用什么样的方式写入流数据,如果YES则表示追加,如果是NO则表示覆盖
         */
        NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:fullPath append:YES];
    
        //只要调用了该方法就会往文件中写数据
        //如果文件不存在,那么会自动的创建一个
        [stream open];
        self.stream = stream;
    
        //2.当接收到数据的时候写数据
        //使用输出流写数据
        /*
         第一个参数:要写入的二进制数据
         第二个参数:要写入的数据的大小
         */
        [self.stream write:data.bytes maxLength:data.length];
    
        //3.当文件下载完毕的时候关闭输出流
        //关闭输出流
        [self.stream close];
        self.stream = nil;
  • 相关阅读:
    [C++]仿java.lang.String的字符串工具类[原]
    SQL基础1创建表、用户
    Linux中gdb 查看core堆栈信息
    Direct3D9基础工具类[原]
    eclipse3.4启动错误
    ndk连接第三方库
    数据库基本概念
    MySQL常见命令
    MySQL启动和停止
    MySQL配置文件
  • 原文地址:https://www.cnblogs.com/liugengqun/p/5136995.html
Copyright © 2011-2022 走看看