前言:本篇讲解,在前篇iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载的基础上,使用输出流代替文件句柄实现大文件断点续传。
在实际开发中,输入输出流用的比较少,但是用起来也是很方便的。iOS开发用到的输入输出流和在Java中的输入输出流是几乎一样的,本质也是一个意思:将网络返回的数据当做流来处理。
输入输出的理解:输入到哪里?输出到哪里?这个问题不难理解,输入输出是要站着服务器角度来思考的,下面用图来解释:

代码关键词:
1、在接收到响应头的代理方法里创建输出流(根据上面的图,下载自然需要创建输出流NSOutputStream)。

2、在接收数据的代理方法中写(write)数据,注意写入的是data字节(data.bytes)。

3、最后在下载完毕的代理方法里关闭输出流。

用来做代码练习的API接口:
MP4小视频:http://120.25.226.186:32812/resources/videos/minion_01.mp4
完整的关键代码:
#import "ViewController.h" @interface ViewController () @property (nonatomic ,assign)NSInteger totalSzie; @property (nonatomic ,assign)NSInteger currentSzie; @property (nonatomic, strong) NSString *fileName; /** 文件的路径*/ @property (nonatomic ,strong) NSString *fullPath; /** 请求对象*/ @property (nonatomic ,strong)NSURLConnection *connect; /** 输出流*/ @property (nonatomic ,strong)NSOutputStream *stream; @property (weak, nonatomic) IBOutlet UIProgressView *progressView; @end @implementation ViewController #pragma mark ---------------------- #pragma mark Events - (IBAction)downloadBtnClick:(id)sender { // [[NSFileManager defaultManager] removeItemAtPath:self.fullPath error:nil]; [self download]; } - (IBAction)cancelBtnClick:(id)sender { //取消网络请求 [self.connect cancel]; } #pragma mark ---------------------- #pragma mark Methods -(void)download { NSLog(@"------"); //1.确定url NSURL *url =[NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"]; //2.创建请求对象 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; //设置请求头信息,说明只需要请求该资源的一部分数据 /* bytes=0-1000 表示下载0~1000的数据 bytes=0- 表示从0开始下载直到下载完毕 bytes=100- 表示从0开始下载直到下载完毕 */ NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentSzie]; [request setValue:range forHTTPHeaderField:@"Range"]; NSLog(@"%@",range); //3.发送异步请求 self.connect = [NSURLConnection connectionWithRequest:request delegate:self]; } #pragma mark ---------------------- #pragma mark NSURLConnectionDataDelegate -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { NSLog(@"--didReceiveResponse-"); //判断是否已经下载过了 if (self.currentSzie >0) { return; } //0.获得文件的总大小 //expectedContentLength是本次请求的数据的大小,并不是整个 self.totalSzie = response.expectedContentLength; //1.得到文件的名称 self.fileName = response.suggestedFilename; //2.获得文件的全路径 //caches NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; NSString *fullPath = [caches stringByAppendingPathComponent:self.fileName]; self.fullPath = fullPath; //3.创建输出流 /* 第一个参数: 写入数据的地址 第二个参数: 表示要不要追加 断点续传肯定要追加 */ NSOutputStream *stream = [[NSOutputStream alloc]initToFileAtPath:fullPath append:YES]; self.stream = stream; //4.打开数据流 // 如果文件不存在,那么会自动创建一个空的文件 [self.stream open]; } -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { //写数据 /* 第一个参数:要写的数据 第二个参数:数据的长度 */ [self.stream write:data.bytes maxLength:data.length]; //3.累加当前下载的数据大小 self.currentSzie +=data.length; //4.计算文件的下载进度 NSLog(@"%f",1.0 * self.currentSzie / self.totalSzie); self.progressView.progress = 1.0 * self.currentSzie / self.totalSzie; } -(void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"%@",self.fullPath); //1.关闭输出流 [self.stream close]; //2.清空指针 self.stream = nil; } -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { } @end
AI 代码解读