使用NSURLConnection解决下载的问题——同步解决下载内存峰值与进度跟进的问题(只需了解原理)...

下载中涉及到的问题:

问题一:直接使用Connection块操作进行下载,不能实现进度的跟进。——导致用户体验不好。

解决方法:通过代理的方式来处理网络数据。

问题二:当用代理的方式进行处理网络数据,如果每传过来一个数据包就添加在全局数据Data的后面 最后全部下载完成后才把连接后的NSData全局数据写入到指定路径,这样做会存在峰值问题。因为全部都添加在一起都在内存中,导致下载完成后的某一时刻内存占用很大。

解决方法:利用句柄NSFileHandle进行逐步写入,即每下载一点就写进文件,然后把句柄移到文件末尾,继续写入。

注意:如果不使用句柄的话,后面写进去的数据会把前面数据给覆盖掉。如果使用句柄,每次写入都会在句柄之后写入数据。句柄的创建方法:+ (instancetype)fileHandleForWritingAtPath:(NSString *)path;但是必须注意的是——如果指定路径path所在的文件不存在的话,那么创建的句柄对象就是nil,所以需要先判断句柄是否为nil,如果为nil的话就要用writeToFile方法写入一次先生成文件,其后每次写入数据的时候都使用句柄。具体方法如下:

//如果文件不存在,句柄就是nil。这时候就无法操作文件

if (fp ==nil) {

[data writeToFile:self.targetPathatomically:YES];

}else

{

//将句柄移到当前文件的末尾

[fp seekToEndOfFile];

//将数据写入(是以句柄作参照来开始写入的)

[fp writeData:data];

//C语言中,所有的文件操作完成以后,都需要关闭文件,这里也需要关闭。

//为了保证文件的安全

[fp closeFile];

}


实现跟踪进度下载需要让当前控制器遵守NSURLConnectionDataDelegate协议,并实现内部的四个代理方法如下:

代理方法(一)当客户端接收到服务器响应后首先调用的方法:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;

代理方法(二)当客户端接受响应后会不断接服务器发送来的数据,会不断地调用didReceiveData方法:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;

代理方法(三)当所有的数据传输完毕后会调用connectionDidFinishLoading方法:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection;

代理方法(四)如果下载过程中出现了错误,就会调用didFailWithError方法:

-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;

调用过程概述:

客户端发送一个请求到服务器,服务器接收到请求后返回一个响应。在第一个代理方法didReceiveResponse中就会接收到服务器端的响应信息,服务器发给客户端的响应包含一个响应头(响应头里包含了文件的大小)。

然后服务器将整个文件打包成很多小的二进制数据包,不断地调用第二个代理方法进行发送(每发送一个数据包都会调用一次didReceiveData代理方法)。当所有的数据包都传输完毕后就会调用connectionDidFinishLoading这个方法。如果在下载的过程中出现了错误,就会调用didFailWithError这个方法。

代理中需要执行的操作如下:

一般在第一个代理方法中可以实现获取下载文件的总长度与服务器建议的文件名、将进度清零、设置下载文件后的保存路径、删除保存路径的同名同类型的文件的操作。

一般在第二个代理方法中可以实现累加记录当前文件的长度并计算进度、利用文件管理类NSFIleManager和句柄操作类NSFileHandle进行文件的逐步写入等操作。

一般在第三个代理方法中实现给用户下载完成的提示也可以发送通知。

一般在第四个代理方法中实现后出现错误后的处理步骤。

首先打开本地服务器(自己电脑的后台服务器),并拖进服务器端文件夹一个视频文件。打开服务器验证如下:


然后打开本地服务器。

具体代码如下:

<span style="font-size:18px;"><span style="font-size:18px;">//
//  ViewController.m
//  使用NSURLConnection进行下载
//
//  Created by apple on 15/10/29.
//  Copyright (c) 2015年 LiuXun. All rights reserved.
//
/**
 IOS5.0之前, 网络的下载是一个黑暗的时代
 但是,需要了解思路
 
 NSURLConnection下载存在的问题,IOS2.0就有了。专门负责网络的传输,已经有10多年的历史了。
 特点:
 - 处理简单的网络操作,非常简单。
 - 但是处理复杂的网络操作非常繁琐。
 
 ASI和AFN第三方框架才会出现。
 *** IOS5.0以前通过代理的方式处理网络的数据
 
 存在的问题:
 1. 下载的过程中,没有进度的跟进—— 导致用户体验不好
 2.  存在内存的峰值
 
 解决进度跟进的问题:
 解决的办法:通过代理的方式来处理网络数据
 
 代理还是出现内存峰值,是因为全部接受了,再去写入
 解决办法:接受到一点写一点。
 */

#import "ViewController.h"
#import <Foundation/Foundation.h>
/**
 NSURLConnectionDownloadDelegate :此代理协议只适用于杂志的下载,国内很少有人开发。
 */
@interface ViewController ()<NSURLConnectionDataDelegate>

// 下载文件的总长度
@property(nonatomic, assign) long long expectedContentLength;

// 当前下载的长度
@property(nonatomic, assign) long long currentLength;

// 保存文件的目标路径
@property(nonatomic, strong)NSString *targetPath;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 下载使用GET请求
    
    // 1. url
    NSString *urlStr = @"http://127.0.0.1/07-知识点勘误.mp4";
    // 因为网址中含有中文,所以要进行转义——使用stringByAddingPercentEscapesUsingEncoding方法
    urlStr  =[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    
    NSURL *url = [NSURL URLWithString:urlStr];
    
    // 2. 请求
    NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:1 timeoutInterval:2.0f];
    
    // 3. 连接
    //    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    //
    //       // 把数据保存到桌面
    //        [data writeToFile:@"/Users/apple/Desktop/123.mp4" atomically:YES];
    //
    //        NSLog(@"下载完成");
    //    }];
    
    // 3 、创建连接
    NSURLConnection *connect = [NSURLConnection connectionWithRequest:request delegate:self];
    
    // 4、启动网络连接
    [connect start];
}

#pragma mark- 实现方法

// 1、接受到服务器的响应— 做好准备
/**
 接收到服务器响应后首先调用这个方法
 
 NSURLResponse  响应
 - (instancetype)initWithURL:(NSURL *)URL MIMEType:(NSString *)MIMEType expectedContentLength:(NSInteger)length textEncodingName:(NSString *)name;
 
 URL:资源路径
 MIMEType(Content-Type):返回的二进制数据类型
 expectedContentLength:预期的文件长度,对于下载来说就是文件的大小
 textEncodingName:文本的编码名称
 suggestedFileName:服务器建议的文件名
 
 UTF-8 :几乎涵盖了全世界200多个国家的语言文字
 GB2312:国内的一些老的网站可能还在使用这个编码  包含了6700多个汉字
 */
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    //    NSLog(@"%@", response);
    
    // 文件的大小
    NSLog(@"%lld", response.expectedContentLength);
    
    // 记录文件的总长度
    self.expectedContentLength = response.expectedContentLength;
    
    // 将下载的长度清零
    self.currentLength = 0;
    
    // 设置文件的目标路径
    self.targetPath = [@"/Users/apple/Desktop"  stringByAppendingPathComponent: response.suggestedFilename];
    
    // 简单粗暴,准备接收文件数据之前,直接删掉
    [[NSFileManager  defaultManager] removeItemAtPath:self.targetPath error:NULL];
}

// 2、接收到服务器返回的数据— 会被调用多次
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSLog(@"接收到数据 长度为%tu, 可以拼接所有数据", data.length);
    
    // 记录当前已经下载文件的长度
    self.currentLength += data.length;
    
    // 计算进度
    float progress = (float)self.currentLength/self.expectedContentLength;
    NSLog(@"进度%f", progress);
    
    // 拼接数据
    /*
     NSFileManager —> 做文件获取文件夹的复制和删除以及路径文件夹的操作
     
     NSFileHandle —> 文件写入句柄 操作文件
     */
    //     建立文件句柄, 准备写入到targetPath
    NSFileHandle *fp = [NSFileHandle fileHandleForWritingAtPath:self.targetPath];
    
    //  如果文件不存在,句柄就是nil。这时候就无法操作文件
    if (fp == nil) {
        [data writeToFile:self.targetPath atomically:YES];
    }else
    {
        // 将句柄移到当前文件的末尾
        [fp seekToEndOfFile];
        
        // 将数据写入(是以句柄作参照来开始写入的)
        [fp writeData:data];
        
        
        // 在C语言中,所有的文件操作完成以后,都需要关闭文件,这里也需要关闭。
        // 为了保证文件的安全
        [fp closeFile];
    }
    
}


// 3、所有的数据传输完毕
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"所有的数据传输完毕,写成文件");
}

// 4、下载过程中出现错误后会调用这个方法
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"注意网络请求的过程中,一定要注意错误处理");
}
@end

/**
 客户端发送一个请求到服务器,服务器接收到请求后返回一个响应。在第一个代理方法didReceiveResponse中就会接收到服务器端的响应信息,服务器发给客户端的响应包含一个响应头 (响应头里包含了文件的大小)。
 然后服务器将整个文件打包成很多小的二进制数据包,不断地调用第二个代理方法进行发送(每发送一个数据包都会调用一次didReceiveData代理方法)。当所有的数据包都传输完毕后就会调用connectionDidFinishLoading这个方法。如果在下载的过程中出现了错误,就会调用didFailWithError这个方法。
 
 */</span></span>
运行完成后,指定的桌面路径发现了对应的文件。控制台输出如下:




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AFNetworking是一个基于Objective-C语言的iOS网络请求框架,它提供了一系列方便的API,帮助开发者快速地进行网络请求并处理响应内容。其原理是基于NSURLConnectionNSURLSession建立的网络请求,通过对网络请求的封装,提供了比原生API更加便捷的操作方式。 常用操作包括: 1. 发起网络请求:使用AFHTTPRequestOperationManager或AFHTTPSessionManager类的GET、POST等方法,可以方便地发起网络请求。 2. 设置请求参数:使用AFHTTPRequestOperationManager或AFHTTPSessionManager类的setParameterEncoding方法或AFHTTPRequestSerializer类的setQueryStringSerializationWithStyle方法,可以设置请求参数的编码格式。 3. 设置请求头:使用AFHTTPRequestOperationManager或AFHTTPSessionManager类的setValue:forHTTPHeaderField方法,可以设置请求头信息。 4. 处理响应数据:使用AFHTTPRequestOperation类的completionBlock或AFHTTPSessionManager类的dataTaskWithRequest:completionHandler方法,可以处理请求响应数据。 5. 下载文件:使用AFHTTPRequestOperation类的setDownloadProgressBlock和setCompletionBlockWithSuccess方法或AFHTTPSessionManager类的downloadTaskWithRequest:progress:destination:completionHandler方法,可以实现文件下载功能。 6. 上传文件:使用AFHTTPRequestOperationManager或AFHTTPSessionManager类的POST方法和AFHTTPRequestSerializer类的multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock方法,可以实现文件上传功能。 总体来说,AFNetworking提供了丰富的API和良好的封装,使得iOS开发者能够更加方便地进行网络请求和处理响应数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值