NSURLConnection相关用法

NSURLConnection和Runloop

  • 两种为NSURLConnection设置代理方式的区别

    //第一种设置方式:

    //通过该方法设置代理,会自动的发送请求

    // [[NSURLConnection alloc]initWithRequest:request delegate:self];



    //第二种设置方式:

    //设置代理,startImmediately为NO的时候,该方法不会自动发送请求

    NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];

    //手动通过代码的方式来发送请求

    //注意该方法内部会自动的把connect添加到当前线程的RunLoop中在默认模式下执行

    [connect start];

  • 如何控制代理方法在哪个线程调用

    //说明:默认情况下,代理方法会在主线程中进行调用(为了方便开发者拿到数据后处理一些刷新UI的操作不需要考虑到线程间通信)

    //设置代理方法的执行队列

    [connect setDelegateQueue:[[NSOperationQueue alloc]init]];

  • 开子线程发送网络请求的注意点,适用于自动发送网络请求模式

//在子线程中发送网络请求-调用startf方法发送

-(void)createNewThreadSendConnect1

{

    //1.创建一个非主队列

    NSOperationQueue *queue = [[NSOperationQueue alloc]init];

    //2.封装操作,并把任务添加到队列中执行

    [queue addOperationWithBlock:^{

        NSLog(@"%@",[NSThread currentThread]);

        //2-1.确定请求路径

        NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=dd&pwd=ww&type=JSON"];

        //2-2.创建请求对象

        NSURLRequest *request = [NSURLRequest requestWithURL:url];

        //2-3.使用NSURLConnection设置代理,发送网络请求

        NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];

        //2-4.设置代理方法在哪个队列中执行,如果是非主队列,那么代理方法将再子线程中执行

        [connection setDelegateQueue:[[NSOperationQueue alloc]init]];

        //2-5.发送网络请求

        //注意:start方法内部会把当前的connect对象作为一个source添加到当前线程对应的runloop中

        //区别在于,如果调用start方法开发送网络请求,那么再添加source的过程中,如果当前runloop不存在

        //那么该方法内部会自动创建一个当前线程对应的runloop,并启动。

        [connection start];

    }];

}

//在子线程中发送网络请求-自动发送网络请求

-(void)createNewThreadSendConnect2

{

    NSLog(@"-----");

    //1.创建一个非主队列

    NSOperationQueue *queue = [[NSOperationQueue alloc]init];

    //2.封装操作,并把任务添加到队列中执行

    [queue addOperationWithBlock:^{

        //2-1.确定请求路径

        NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=dd&pwd=ww&type=JSON"];

        //2-2.创建请求对象

        NSURLRequest *request = [NSURLRequest requestWithURL:url];

        //2-3.使用NSURLConnection设置代理,发送网络请求

        //注意:该方法内部虽然会把connection添加到runloop,但是如果当前的runloop不存在,那么不会主动创建。

        NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];

        //2-4.设置代理方法在哪个队列中执行,如果是非主队列,那么代理方法将再子线程中执行

        [connection setDelegateQueue:[[NSOperationQueue alloc]init]];

        //2-5 创建当前线程对应的runloop,并开启

       [[NSRunLoop currentRunLoop]run];

    }];

}

实现断点续传代码

  • 实现思路
    • 在下载文件的时候不再是整块的从头开始下载,而是看当前文件已经下载到哪个地方,然后从该地方接着往后面下载。可以通过在请求对象中设置请求头实现。
  • 解决方案(设置请求头)

//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"];

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


//获得当前要下载文件的总大小(通过响应头得到)

    NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;

    //注意点:res.expectedContentLength获得是本次请求要下载的文件的大小(并非是完整的文件的大小)

    //因此:文件的总大小 == 本次要下载的文件大小+已经下载的文件的大小

    self.totalLength = res.expectedContentLength + self.currentLength;

    NSLog(@"----------------------------%zd",self.totalLength);

    //0 判断当前是否已经下载过,如果当前文件已经存在,那么直接返回

    if (self.currentLength >0) {

        return;

    }

  • 完整代码

#import "ViewController.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIProgressView *progressView;

@property (nonatomic, assign) NSInteger totalSize;

@property (nonatomic, assign) NSInteger currentSize;

/** 文件句柄*/

@property (nonatomic, strong)NSFileHandle *handle;

/** 沙盒路径 */

@property (nonatomic, strong) NSString *fullPath;

/** 连接对象 */

@property (nonatomic, strong) NSURLConnection *connect;

@end

@implementation ViewController

- (IBAction)startBtnClick:(id)sender {

    [self download];

}

- (IBAction)cancelBtnClick:(id)sender {

    [self.connect cancel];

}

- (IBAction)goOnBtnClick:(id)sender {

    [self download];

}

//内存飙升

-(void)download

{

    //1.url

    // NSURL *url = [NSURL URLWithString:@"http://imgsrc.baidu.com/forum/w%3D580/sign=54a8cc6f728b4710ce2ffdc4f3cec3b2/d143ad4bd11373f06c0b5bd1a40f4bfbfbed0443.jpg"];

    

    NSURL *url = [NSURL URLWithString:@"http://www.33lc.com/article/UploadPic/2012-10/2012102514201759594.jpg"];

    

    //2.创建请求对象

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    

    //设置请求头信息,告诉服务器值请求一部分数据range

    /*

     bytes=0-100 

     bytes=-100

     bytes=0- 请求100之后的所有数据

     */

    NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentSize];

    [request setValue:range forHTTPHeaderField:@"Range"];

    NSLog(@"+++++++%@",range);

    

    //3.发送请求

    NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:request delegate:self];

    self.connect = connect;

}

#pragma mark ----------------------

#pragma mark NSURLConnectionDataDelegate

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

{

    NSLog(@"didReceiveResponse");

    

    //1.得到文件的总大小(本次请求的文件数据的总大小 != 文件的总大小)

    // self.totalSize = response.expectedContentLength + self.currentSize;

    

    if (self.currentSize >0) {

        return;

    }

    

    self.totalSize = response.expectedContentLength;

    

    //2.写数据到沙盒中

    self.fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]stringByAppendingPathComponent:@"123.jpg"];

    

    NSLog(@"%@",self.fullPath);

    

    //3.创建一个空的文件

    [[NSFileManager defaultManager] createFileAtPath:self.fullPath contents:nil attributes:nil];

    

    //NSDictionary *dict = [[NSFileManager defaultManager]attributesOfItemAtPath:self.fullPath error:nil];

    

    //4.创建文件句柄(指针)

    self.handle = [NSFileHandle fileHandleForWritingAtPath:self.fullPath];

}

-(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);

    self.progressView.progress = 1.0 *  self.currentSize/self.totalSize;

    //NSLog(@"%@",self.fullPath);

}

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

{

}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection

{

    //1.关闭文件句柄

    [self.handle closeFile];

    self.handle = nil;

    

    NSLog(@"connectionDidFinishLoading");

    NSLog(@"%@",self.fullPath);

}

@end

输出流

  • 使用输出流也可以实现和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;

使用多线程下载文件思路


01 开启多条线程,每条线程都只下载文件的一部分(通过设置请求头中的Range来实现)

02 创建一个和需要下载文件大小一致的文件,判断当前是那个线程,根据当前的线程来判断下载的数据应该写入到文件中的哪个位置。(假设开5条线程来下载10M的文件,那么线程1下载0-2M,线程2下载2-4M一次类推,当接收到服务器返回的数据之后应该先判断当前线程是哪个线程,假如当前线程是线程2,那么在写数据的时候就从文件的2M位置开始写入)

03 代码相关:使用NSFileHandle这个类的seekToFileOfSet方法,来向文件中特定的位置写入数据。

04 技术相关

    a.每个线程通过设置请求头下载文件中的某一个部分

    b.通过NSFileHandle向文件中的指定位置写数据

小文件下载方式

第一种方式(NSData)


//使用NSDta直接加载网络上的url资源(不考虑线程)

-(void)dataDownload

{

    //1.确定资源路径

    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"];

    //2.根据URL加载对应的资源

    NSData *data = [NSData dataWithContentsOfURL:url];

    //3.转换并显示数据

    UIImage *image = [UIImage imageWithData:data];

    self.imageView.image = image;

}

第二种方式(NSURLConnection-sendAsync)


//使用NSURLConnection发送异步请求下载文件资源

-(void)connectDownload

{

    //1.确定请求路径

    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"];

    //2.创建请求对象

    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    //3.使用NSURLConnection发送一个异步请求

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {

        //4.拿到并处理数据

        UIImage *image = [UIImage imageWithData:data];

        self.imageView.image = image;

    }];

}

第三种方式(NSURLConnection-delegate)


//使用NSURLConnection设置代理发送异步请求的方式下载文件

-(void)connectionDelegateDownload

{

    //1.确定请求路径

    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];

    //2.创建请求对象

    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    //3.使用NSURLConnection设置代理并发送异步请求

    [NSURLConnection connectionWithRequest:request delegate:self];

}

  # pragma mark--NSURLConnectionDataDelegate

//当接收到服务器响应的时候调用,该方法只会调用一次

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

{

    //创建一个容器,用来接收服务器返回的数据

    self.fileData = [NSMutableData data];

    //获得当前要下载文件的总大小(通过响应头得到)

    NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;

    self.totalLength = res.expectedContentLength;

    NSLog(@"%zd",self.totalLength);

    //拿到服务器端推荐的文件名称

    self.fileName = res.suggestedFilename;

}

//当接收到服务器返回的数据时会调用

//该方法可能会被调用多次

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

{

//    NSLog(@"%s",__func__);

    //拼接每次下载的数据

    [self.fileData appendData:data];

    //计算当前下载进度并刷新UI显示

    self.currentLength = self.fileData.length;

    NSLog(@"%f",1.0* self.currentLength/self.totalLength);

    self.progressView.progress = 1.0* self.currentLength/self.totalLength;

}

//当网络请求结束之后调用

-(void)connectionDidFinishLoading:(NSURLConnection *)connection

{

    //文件下载完毕把接受到的文件数据写入到沙盒中保存

    //1.确定要保存文件的全路径

    //caches文件夹路径

    NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

    NSString *fullPath = [caches stringByAppendingPathComponent:self.fileName];

    //2.写数据到文件中

    [self.fileData writeToFile:fullPath atomically:YES];

    NSLog(@"%@",fullPath);

}

//当请求失败的时候调用该方法

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

{

    NSLog(@"%s",__func__);

}

你可能感兴趣的:(NSURLConnection相关用法)