前言:本篇讲解,在前篇iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载的基础上,使用输出流代替文件句柄实现大文件断点续传。
在实际开发中,输入输出流用的比较少,但是用起来也是很方便的。iOS开发用到的输入输出流和在Java中的输入输出流是几乎一样的,本质也是一个意思:将网络返回的数据当做流来处理。
输入输出的理解:输入到哪里?输出到哪里?这个问题不难理解,输入输出是要站着服务器角度来思考的,下面用图来解释:
![](https://i-blog.csdnimg.cn/blog_migrate/8b772e57f8c8db900beff471fb6d37f9.png)
代码关键词:
1、在接收到响应头的代理方法里创建输出流(根据上面的图,下载自然需要创建输出流NSOutputStream)。
![](https://i-blog.csdnimg.cn/blog_migrate/7c4a19ad262ba52e5fdf21e725fce811.png)
2、在接收数据的代理方法中写(write)数据,注意写入的是data字节(data.bytes)。
![](https://i-blog.csdnimg.cn/blog_migrate/806198ca164af2201de80630ab22a97a.png)
3、最后在下载完毕的代理方法里关闭输出流。
![](https://i-blog.csdnimg.cn/blog_migrate/7546477b67cd4db9cb5a273fbafde108.png)
用来做代码练习的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