作者:Mitchell
一、NSURLConnection
- iOS7之后不建议使用
- GET请求
- 发送同步请求
// 1.创建一个URL
NSURL *url = [NSURL URLWithString:@"httpAddress"];
// 2.根据URL创建NSURLRequest对象
// 默认情况下NSURLRequest会自动给我们设置好请求头
// request默认情况下就是GET请求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 3.利用NSURLConnection对象发送请求
/*
第一个参数: 需要请求的对象
第二个参数: 服务返回给我们的响应头信息
第三个参数: 错误信息
返回值: 服务器返回给我们的响应体
*/
// NSURLResponse *response = nil;
NSHTTPURLResponse *response = nil; // 真实类型
// 注意点: 如果是调用NSURLConnection的同步方法, 会阻塞当前线程
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
- 发送异步请求
// 1.创建一个URL
NSURL *url = [NSURL URLWithString:@"httpAddress"];
// 2.根据URL创建NSURLRequest对象
// 默认情况下NSURLRequest会自动给我们设置好请求头
// request默认情况下就是GET请求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 3.利用NSURLConnection对象发送请求
/*
第一个参数: 需要请求的对象
第二个参数: 回调block的队列, 决定了block在哪个线程中执行
第三个参数: 回调block
*/
// 注意点: 如果是调用NSURLConnection的同步方法, 会阻塞当前线程
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
/*
response: 响应头
data : 响应体
connectionError : 错误信息
*/
// NSLog(@"%@", [NSThread currentThread]);
NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
- POST请求:
// 1.创建一个URL
NSURL *url = [NSURL URLWithString:@"http://admin/login"];
// 2.根据URL创建NSURLRequest对象
// NSURLRequest *request = [NSURLRequest requestWithURL:url];
/*
NSMutableURLRequest中保存了请求的地址/请求头/请求体
*/
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 2.1设置请求方式
// 注意: POST一定要大写
request.HTTPMethod = @"POST";
// 2.2设置请求体
// 注意: 如果是给POST请求传递参数: 那么不需要写?号
request.HTTPBody = [@"username=Mitchell&pwd=123456&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
// 3.利用NSURLConnection对象发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
- NSURLConnection 与 NSRunLoop 的关联使用
- 主要是区分 NSURLConnection 在主线程和子线程发送网络请求的区别
- 主线程
- 1、 直接发送网络请求,发送是异步的,但是代理方法是在主线程中执行的:
NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
NSURLRequest*request = [NSURLRequest requestWithURL:url];
//这里分两种方式发送请求
//2.1 直接发送网络请求是异步的,但是回调方法是在主线程中执行的
//[[NSURLConnection alloc]initWithRequest:request delegate:self];
* 2、如果按照如下设置,那么回调的代理方法也会运行在子线程中:
NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
NSURLRequest*request = [NSURLRequest requestWithURL:url];
//2.2 设置回调方法也在子线程中运行
NSURLConnection*conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
[conn setDelegateQueue:[[NSOperationQueue alloc] init]];
[conn start];
- 子线程
* `因为 NSURLConnection 是局部变量`,当我们创建的时候其实是会默认`添加到当前的 RunLoop 中`,如果是在主线程添加,主线程的 RunLoop 是默认有的,无须我们创建,`然而如果再子线程中,是默认没有 RunLoop 和输入源的,所以需要给子线程手动添加 RunLoop` 。
* 为什么使用 `start` 方法就可以呢?
因为 start
方法如果没有 RunLoop ,会默认添加一个 RunLoop 到当前的线程中来。
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
NSURLRequest*request = [NSURLRequest requestWithURL:url];
//2.1 这么设置还是可以的
//NSURLConnection*conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
//[conn setDelegateQueue:[[NSOperationQueue alloc] init]];
//[conn start];
//2.2 但是这样设置就不行了
//[NSURLConnection connectionWithRequest:request delegate:self];
/*问题一:为什么?
NSURLConnection 是临时变量,当运行的时候被默认添加到当前线程的 RunLoop 中,由于主线程的RunLoop是默认存在的,所以可以运行,这里不行能的原因就是当前线程并没有 RunLoop,如果想让其运行,比需要创建RunLoop。
问题二:为什么2.1就可以?
因为start方法:如果没有一个runloop,它会自动给当前线程添加runLoop,然后将connection加到runLoop中。
If you don’t schedule the connection in a run loop or an operation queue before calling this method, the connection is scheduled in the current run loop in the default mode.
*/
//2.2解决方式
NSRunLoop*loop = [NSRunLoop currentRunLoop];
[NSURLConnection connectionWithRequest:request delegate:self];
[loop run];
});
二、NSURLSession
- 推荐使用
- 简单使用
- Get
- Get
//注意:如果需要改请求头 需要使用这种方法
NSURL*url = [NSURL URLWithString:@"httpAddress"];
NSURLRequest*request = [NSURLRequest requestWithURL:url];
//1、创建NSURLSession
NSURLSession*session = [NSURLSession sharedSession];
//2、利用NSURLSession创建Tash
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
/*
data:服务器返回高i的数据
response:响应头
error:错误信息
*/
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}];
//3、执行Task
[task resume];
*
NSURL*url = [NSURL URLWithString:@"httpAddress"];
//1、创建NSURLSession
NSURLSession*session = [NSURLSession sharedSession];
//2、利用NSURLSession创建Tash
// 如果是通过url的方法创建Task,方法内部会自动根据URL创建一个request
// 如果是发送Get请求,或者不需要设置请求头信息,那么建议使用当前方法发送请求
NSURLSessionDataTask*task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}];
//3、执行Task
[task resume];
- POST
NSURL*url = [NSURL URLWithString:@"httpAddress"];
NSMutableURLRequest*request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody =@"需要发送的信息";
//1、创建NSURLSession
NSURLSession*session = [NSURLSession sharedSession];
//2、利用NSURLSession创建Tash
NSURLSessionDataTask*task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}];
//3、执行Task
[task resume];
- 断点下载
#import "ViewController.h"
#import "NSString+Mitchell.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIProgressView *pro;
@property (weak, nonatomic) IBOutlet UIButton *startBtn;
@property (weak, nonatomic) IBOutlet UIButton *cancelBtn;
@property (nonatomic,strong) NSURLSession * session;
@property(nonatomic,assign)CGFloat totalLength;
@property(nonatomic,assign)CGFloat currentLength;
@property(nonatomic,strong)NSURLSessionDataTask*task;
@property(nonatomic,strong)NSOutputStream * stream;
@property(nonatomic,strong)NSString * path;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//初始化操作
//1、初始化文件路径
self.path = [@"abc.mp4" cacheDir];
//2、初始化当前下载进度
self.currentLength = [self getFileSizeWithPath:self.path];
}
#pragma mark ------------------ delegate ------------------
//注意:NSURLSessionDataTask的代理方法中,默认情况下是不接受服务器返回的数据的,所以didCompleteWithError、didReceiveData默认不会被调用
//如果想接受服务器返回的数据,必须手动告诉系统,我们需要接受数据
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
NSLog(@"didReceiveResponse");
self.totalLength = response.expectedContentLength;
//告诉服务器接受,才能调用didCompleteWithError、didReceiveData两个方法
NSString*path = [@"abc.mp4" cacheDir];
NSLog(@"%@",path);
_stream = [NSOutputStream outputStreamToFileAtPath:path append:YES];
[_stream open];
completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
self.currentLength +=data.length;
self.pro.progress = 1.0*_currentLength/_totalLength;
[_stream write:data.bytes maxLength:data.length];
NSLog(@"didReceiveData");
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
NSLog(@"didCompleteWithError");
[_stream close];
_stream = nil;
}
- (NSURLSession *)session{
if (!_session) {
//1、创建NSURLSession
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
- (NSURLSessionDataTask *)task{
if (!_task) {
//图片:http://img1.imgtn.bdimg.com/it/u=298400068,822827541&fm=21&gp=0.jpg%2F2008-09-08%2F200898163242920_2.jpg&bdtype=0&fr=ala&ala=1&alatpl=others&pos=1
//MP4:http://mvvideo1.meitudata.com/55d99e5939342913.mp4
NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
NSMutableURLRequest*request = [NSMutableURLRequest requestWithURL:url];
NSString*range = [NSString stringWithFormat:@"bytes:%zd-",[self getFileSizeWithPath:self.path]];
[request setValue:range forHTTPHeaderField:@"Range"];
//2、利用NSURLSession创建Tash
_task = [self.session dataTaskWithURL:url];
}
return _task;
}
- (NSUInteger)getFileSizeWithPath:(NSString*)path{
NSUInteger currentSize = [[[NSFileManager defaultManager]attributesOfItemAtPath:path error:nil][NSFileSize] integerValue];
return currentSize;
}
#pragma mark ------------------ 开始 ------------------
- (IBAction)start:(id)sender {
//3、执行Task
[self.task resume];
}
#pragma mark ------------------ 暂停 ------------------
- (IBAction)pause:(id)sender {
NSLog(@"暂停");
[self.task suspend];
}
#pragma mark ------------------ 继续 ------------------
- (IBAction)resume:(id)sender {
NSLog(@"继续");
[self.task resume];
}
#pragma mark ------------------ 取消 ------------------
- (IBAction)cancel:(id)sender {
}
@end
- 下载进度
#import "ViewController.h"
#import "NSString+Mitchell.h"
@interface ViewController ()
@property (weak, nonatomic ) IBOutlet UIProgressView *pro;
@property (weak, nonatomic ) IBOutlet UIButton *startBtn;
@property (weak, nonatomic ) IBOutlet UIButton *cancelBtn;
@property (nonatomic,strong) NSURLSessionDownloadTask * task;
@property (nonatomic,strong) NSData * resumeData;
@property (nonatomic,strong) NSURLSession * session;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark ------------------ didFinishDownloadingToURL ------------------
//下载完成时调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location{
NSString*toPath = [@"abc.mp4" cacheDir];
NSURL*toUrl = [NSURL fileURLWithPath:toPath];
NSFileManager *manager = [NSFileManager defaultManager];
[manager moveItemAtURL:location toURL:toUrl error:nil];
NSLog(@"%@",toUrl);
NSLog(@"didFinishDownloadingToURL");
_startBtn.userInteractionEnabled = YES;
}
#pragma mark ------------------ didWriteData ------------------
//接收服务器返回的数据时调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
//bytesWritten:每次接收了多少
//totalBytesWritten:本地写入了多少
//totalBytesExpectedToWrite:一共多少
NSLog(@"%zd,%zd,%zd",bytesWritten,totalBytesWritten,totalBytesExpectedToWrite);
self.pro.progress = 1.0* totalBytesWritten/totalBytesExpectedToWrite;
}
#pragma mark ------------------ didResumeAtOffset ------------------
//恢复下载时候调用(使用resumeData恢复下载的时候才能调用)
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
NSLog(@"didResumeAtOffset");
}
//下载完成时调用
//如果下载时候error有值,代表着下载出错
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
NSLog(@"didCompleteWithError");
_startBtn.userInteractionEnabled = YES;
}
#pragma mark ------------------ 开始 ------------------
- (IBAction)start:(id)sender {
//图片:http://img1.imgtn.bdimg.com/it/u=298400068,822827541&fm=21&gp=0.jpg%2F2008-09-08%2F200898163242920_2.jpg&bdtype=0&fr=ala&ala=1&alatpl=others&pos=1
//MP4:http://mvvideo1.meitudata.com/55d99e5939342913.mp4
NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
NSURLRequest*request = [NSURLRequest requestWithURL:url];
//1、创建NSURLSession
/*
第一个参数:Session的配置信息
第二个参数:代理
第三个参数:规定了代理方法在哪个线程中执行
*/
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//2、利用NSURLSession创建Task
_task = [self.session downloadTaskWithRequest:request];
//3、执行Task
[_task resume];
_startBtn.userInteractionEnabled = NO;
}
#pragma mark ------------------ 暂停 ------------------
- (IBAction)pause:(id)sender {
NSLog(@"暂停");
[_task suspend];
}
#pragma mark ------------------ 继续 ------------------
- (IBAction)resume:(id)sender {
NSLog(@"继续");
// [_task resume];
//恢复信息恢复下载,用获取到resumeData继续下载
self.task = [_session downloadTaskWithResumeData:_resumeData];
[self.task resume];
}
#pragma mark ------------------ 取消 ------------------
- (IBAction)cancel:(id)sender {
//取消
//注意点:一旦取消,就不能恢复了
// [_task cancel];
//如果是调用了cancelByProducingResumeData方法,方法内部会回调一个block,在block中会将resumeData传递给我们
//resumeData中就保存了当前下载任务的配置信息(下载到什么地方,从什么地方恢复等等)
[self.task cancelByProducingResumeData:^(NSData *resumeData) {
self.resumeData = resumeData;
}];
}
@end
- 上传
#import "ViewController.h"
// 请求头的
#define MitchellHeaderBoundary @"----xiaomage"
// 请求体的
#define MitchellBoundary [@"------xiaomage" dataUsingEncoding:NSUTF8StringEncoding]
// 结束符
#define MitchellEndBoundary [@"------xiaomage--" dataUsingEncoding:NSUTF8StringEncoding]
// 将字符串转换为二进制
#define MitchellEncode(str) [str dataUsingEncoding:NSUTF8StringEncoding]
// 换行
#define MitchellNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSURL*url = [NSURL URLWithString:@""];
NSMutableURLRequest*request = [NSMutableURLRequest requestWithURL:url];
//1、创建Session
NSURLSession*session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//2、根据Seesion创建Task
/*
//注意:fromFile方法是用于PUT请求上传文件的
//而我们的服务器只支持POST请求上传文件
NSURL*fileUrl = [NSURL fileURLWithPath:@""];
NSURLSessionUploadTask*task = [session uploadTaskWithRequest:request fromFile:fileUrl completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
}];
*/
/*请求体
第一个参数:需要请求的地址/请求头/
第二个参数:需要上传拼接之后的二进制数据
*/
request.HTTPMethod = @"POST";
// NSURL*fileUrl = [NSURL fileURLWithPath:@""];
// NSData*data =[NSData dataWithContentsOfURL:fileUrl];
// 2.2设置请求体
NSMutableData *data = [NSMutableData data];
// 2.2.1设置文件参数
[data appendData:MitchellBoundary];
[data appendData:MitchellNewLine];
// name : 对应服务端接收的字段类型(服务端参数的名称)
// filename: 告诉服务端当前的文件的文件名称(也就是告诉服务器用什么名称保存当前上传的文件)
[data appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"videos.plist\"" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:MitchellNewLine];
[data appendData:[@"Content-Type: application/octet-stream" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:MitchellNewLine];
[data appendData:MitchellNewLine];
//上传文件的数据
NSURL*fileUrl = [NSURL fileURLWithPath:@""];
NSData*imgData = [NSData dataWithContentsOfURL:fileUrl];
[data appendData:imgData];
[data appendData:MitchellNewLine];
[data appendData:MitchellNewLine];
// 2.2.2设置非文件参数
[data appendData:MitchellBoundary];
[data appendData:MitchellNewLine];
// name : 对应服务端接收的字段类型(服务端参数的名称)
[data appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:MitchellNewLine];
[data appendData:MitchellNewLine];
[data appendData:[@"Mitchell" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:MitchellNewLine];
// 2.2.3设置结束符号
[data appendData:MitchellEndBoundary];
//注意点:如果利用NSURLSessionUploadTask上传文件,那么请求体必须卸载fromData参数中,不能设置在request中
//如果设置在request中会被忽略
//
request.HTTPBody = data;
NSURLSessionUploadTask*task = [session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
}];
//3、执行Task
[task resume];
}
#pragma mark ------------------ Delegate ------------------
//上传过程中调用
//bytesSent:当前这一次上传的数据大小
//totalBytesSent:总共已经上传的数据大小
//totalBytesExpectedToSend:需要上传文件的大小
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
NSLog(@"%f",1.0*totalBytesSent/totalBytesExpectedToSend);
}
//请求完毕时调用
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
//用block方式 创建是不会调用这个方法
}
@end