NSData *data = [NSData dataWithContentsOfURL:url]; 使用NSURLConnection 异步连接 NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/resources/images/minion_13.png"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { NSLog(@"---%zd---",data.length); }];
-(void)viewDidLoad { [super viewDidLoad]; // 建立连接 NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/resources/videos/minion_02.mp4"]; [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:url] delegate:self]; } -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response { NSLog(@"didReceiveResponse:%@",response); // 初始化 self.data = [NSMutableData data]; self.movieCount = [response.allHeaderFields[@"Content-Length"] integerValue]; } -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 缓存到内存 [self.data appendData:data]; CGFloat progress = 1.0 * self.data.length / self.movieCount; self.progressView.progress = progress; NSLog(@"%f",progress * 100 ); } -(void)connectionDidFinishLoading:(NSURLConnection *)connection { // 这里进行文件的保存,保存到cache里 NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; [self.data writeToFile:[filePath stringByAppendingPathComponent:@"1.mp4"] atomically:YES]; self.data = nil; }
使用NSURLConnection代理方式实现
// 接收到响应的时候:创建一个空的文件 -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response { // 获取文件长度 self.movieCount = [response.allHeaderFields[@"Content-Length"] integerValue]; // 创建一个空文件 [[NSFileManager defaultManager] createFileAtPath:SLQFilePath contents:nil attributes:nil]; // 创建文件句柄 self.handle = [NSFileHandle fileHandleForWritingAtPath:SLQFilePath]; } // 接收到具体数据:马上把数据写入一开始创建好的文件 -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 移动到文件末尾 [self.handle seekToEndOfFile]; // 写入数据 [self.handle writeData:data]; // self.currentCount += data.length; CGFloat progress = 1.0 * self.currentCount / self.movieCount; self.progressView.progress = progress; NSLog(@"%f",progress * 100 ); } -(void)connectionDidFinishLoading:(NSURLConnection *)connection { self.movieCount = 0; // 关闭文件 [self.handle closeFile]; self.handle = nil; }
解压缩使用第三方库 SSZipArchive
// 1 使用指定文件,创建一个压缩文件 NSArray *paths = @[ @"/Users/song/Desktop/test/1.png", @"/Users/song/Desktop/test/2.png", @"/Users/song/Desktop/test/3.png", @"/Users/song/Desktop/test/4.png", @"/Users/song/Desktop/test/5.png" ]; [Main createZipFileAtPath:@"/Users/song/Desktop/test.zip" withFilesAtPaths:paths]; // 2 使用指定目录创建一个压缩文件 [Main createZipFileAtPath:@"/Users/song/Desktop/test121212.zip" withContentsOfDirectory:@"/Users/song/Desktop/test"]; // 3 解压缩一个文件到指定目录 [Main unzipFileAtPath:@"/Users/song/Desktop/test.zip" toDestination:@"/Users/song/Desktop"];
// 一定要注意这个格式是固定的 /* 文件参数格式 --分割线\r\n Content-Disposition: form-data; name="参数名"; filename="文件名"\r\n Content-Type: 文件的MIMEType\r\n \r\n 文件数据 \r\n */ // 文件上传 // 1、创建请求 NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/upload"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 2、设置协议 request.HTTPMethod = @"POST"; // 3、设置请求头 [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",SLQBoundary] forHTTPHeaderField:@"Content-Type"]; // 4、设置请求体 NSMutableData *body = [NSMutableData data]; // 设置文件参数 // 设置边界 [body appendData:SLQUTF(@"--")]; [body appendData:SLQUTF(SLQBoundary)]; [body appendData:SLQEnter]; // 文件参数名 [body appendData:SLQUTF([NSString[] stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"1.png\""])]; [body appendData:SLQEnter]; // 文件类型 [body appendData:SLQUTF([NSString stringWithFormat:@"Content-Type: image/png"])]; [body appendData:SLQEnter]; // 文件内容 [body appendData:SLQEnter]; UIImage *image = [UIImage imageNamed:@"1"]; [body appendData:UIImagePNGRepresentation(image)]; [body appendData:SLQEnter]; /* 非文件参数格式 --分割线\r\n Content-Disposition: form-data; name="参数名"\r\n \r\n 参数值 \r\n */ // 设置非文件参数 [body appendData:SLQUTF(@"--")]; [body appendData:SLQUTF(SLQBoundary)]; [body appendData:SLQEnter]; [body appendData:SLQUTF([NSString stringWithFormat:@"Content-Disposition: form-data; name=\"username\""])]; [body appendData:SLQEnter]; [body appendData:SLQEnter]; [body appendData:SLQUTF(@"bulabulabula")]; [body appendData:SLQEnter]; /* 结束标记 --分割--线\r\n \r\n */ // 结束标记 [body appendData:SLQUTF(@"--")]; [body appendData:SLQUTF(SLQBoundary)]; [body appendData:SLQUTF(@"--")]; [body appendData:SLQEnter]; request.HTTPBody = body; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { // 这个方法会调用descriptionWithLocale方法,可以在这里解决输出到控制台显示中文乱码的问题 NSLog(@"%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]); }];
获取MIMEType
OC 方法
-(NSString *)getMIMEType:(NSString *)path { NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]]; NSURLResponse *response = nil; [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil]; return response.MIMEType; }
c语言
// 要包含头文件MobileCoreServices.h +(NSString *)mimeTypeForFileAtPath:(NSString *)path { if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { return nil; } CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[path pathExtension], NULL); CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType); CFRelease(UTI); if (!MIMEType) { return @"application/octet-stream"; } return (__bridge NSString *)(MIMEType); }
文件流,文件输出流,可以输出到内存、硬盘、NSData
-(void)viewDidLoad { [super viewDidLoad]; // 建立连接 NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/resources/videos/minion_02.mp4"]; [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:url] delegate:self]; } //接收到响应的时候:创建一个空的文件 -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response { // 获取服务器那里给出的建议名字 response.suggestedFilename); NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename]; // 创建文件流 self.stream = [[NSOutputStream alloc] initToFileAtPath:path append:YES]; // 打开文件流 [self.stream open]; } //接收到具体数据:马上把数据写入一开始创建好的文件 -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // 参数1要求是bytes [self.stream write:[data bytes] maxLength:data.length]; NSLog(@"---"); } -(void)connectionDidFinishLoading:(NSURLConnection *)connection { // 关闭文件流 [self.stream close]; }
使用NSURLConnection创建的请求,其内部和NSRunLoop有关联,必须保证NSRunLoop处于运行状态,否则代理方法运行起来就会出问题。
如果要在子线程里创建请求,必须要手动启动NSRunLoop,并且要在方法使用结束手动关闭NSRunLoop
// 建立连接 NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/resources/images/minion_05.png"]; NSURLConnection *con = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:url] delegate:self]; // 决定代理方法在哪个队列中执行 [con setDelegateQueue:[[NSOperationQueue alloc] init]]; // 开启子线程runloop self.runloop = CFRunLoopGetCurrent(); CFRunLoopRun();
在代理方法中使用完毕,停止runloop
-(void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"--connectionDidFinishLoading--%@",[NSThread currentThread]); CFRunLoopStop(self.runloop); }
这个是在iOS7之后推出的用于替代NSURLConnection的新类, 推荐掌握这个 。
NSURLSession 主要由两部分组成,一个是Session实例对象,一个是任务。
使用步骤
创建task( dataTaskWithRequest ),启动task( resume )
抽象类,使用其子类( NSURLSessionDataTask 、 NSURLSessionDownloadTask 、 NSURLSessionUploadTask )
大小文件都一样,默认写入到tmp目录下面,下载完毕后要自己移动文件
NSURLSession 代理
初始化时设置代理 <NSURLSessionDataDelegate>
实现过程
接收响应,指定响应方式:取消、下载、变为下载 didReceivedResponse
接收数据 didReceivedData
接收完毕(成功和失败都会进入这个方法) didComplete
这个可以实现大文件下载
大文件断点下载
NSURLSession 的方法: suspend、resume、cancel
resumeData 保存暂停时程序数据状态,取消任务后要根据状态恢复下载
(不好 ,实现复杂)将下载的tmp文件保存到cachae,然后恢复现在时再从cache移动到临时文件
下载失败后要从NSError中获取失败时的恢复数据
何为断点下载
程序因意外事件终止,导致下载被停止,主要考虑用户突然关闭程序。
可以将下载的数据同步到沙盒中,并且记录下载大小,以及记录已下载的文件,每次下载之前都进行扫描一番。
如果下载一半就在请求头里指定要下载的范围
上传
NSURLSessionUploadTast
POST 请求设置注意 methodBody 为上传的参数 fromData
NSURLSessionConfiguration 统一配置(比如可以在这里设置是否允许设置程序使用蜂窝移动网络)
NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/login?username=123&pwd=4324"]; // 1 获得NSURLSession单例对象 NSURLSession *session = [NSURLSession sharedSession]; // 2 创建任务 NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:url] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { // 4 处理数据 NSLog(@"----%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]); }]; // 3 启动任务 [task resume];
// post请求 NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/login?username=123&pwd=4324"]; // 获得NSURLSession单例对象 NSURLSession *session = [NSURLSession sharedSession]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 设置请求头 request.HTTPMethod = @"POST"; // 设置请求体 NSMutableData *body = [NSMutableData data]; [body appendData:[@"username=123&pwd=234" dataUsingEncoding:NSUTF8StringEncoding]]; request.HTTPBody = body; // 创建任务 NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"----%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]); }]; // 启动任务 [task resume];
直接使用 downloadTaskWithURL:url 进行下载,不过下载成功的文件是放在tmp临时目录里面的,一定要及时把文件给移出来。
NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/resources/videos/minion_02.mp4"]; // 获得NSURLSession单例对象 NSURLSession *session = [NSURLSession sharedSession]; // 创建任务 NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename]; // 这个方式下载的文件在tmp文件夹下,要把下载的文件移动到cache中永久保存,参数是 fileURLWithPath,看清了 // loaction 表示在本地临时存储区的路径 [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:filePath] error:nil]; }]; // 启动任务 [task resume];
-(void)viewDidLoad { [super viewDidLoad]; NSURL *url = [NSURL URLWithString:@"http://123.123.123.123/resources/videos/minion_02.mp4"]; // 创建 NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]]; // 创建任务 NSURLSessionDataTask *task = [session dataTaskWithURL:url]; // 启动任务 [task resume]; } // 接收服务器响应,必须手动执行后续的执行方式(允许接收数据还是不允许接收) -(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { NSLog(@"%s",__func__); // 必须手动指定接下来的数据要不要接收 completionHandler(NSURLSessionResponseAllow); } // 接收数据,多次调用 -(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { NSLog(@"%s",__func__); } // 下载完毕后调用,如果失败,看参数error的值 -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { NSLog(@"%s",__func__); } //1.是没用的,因为segment fault会将typedef识别为1 1.typedef NS_ENUM(NSInteger, NSURLSessionResponseDisposition) { NSURLSessionResponseCancel = 0,/* Cancel the load, this is the same as -[task cancel] */ NSURLSessionResponseAllow = 1,/* Allow the load to continue */ NSURLSessionResponseBecomeDownload = 2,/* Turn this request into a download */ } NS_ENUM_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0);
<span style="font-size:14px;color:#009900;"> </span>
<span style="font-size:14px;color:#009900;">转载自:https://segmentfault.com/a/1190000004518728</span>