一、磁盘缓存:
#import "ViewController.h"
#import "XMGApp.h"
@interface ViewController ()
@property (nonatomic, strong) NSArray *apps;
@property (nonatomic, strong) NSMutableDictionary *images;
@end
@implementation ViewController
-(NSMutableDictionary *)images{
if (_images == nil) {
_images = [NSMutableDictionary dictionary];
}
return _images;
}
-(NSArray *)apps
{
if (_apps == nil) {
//加载数据(字典数组)
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]];
//字典转模型(字典数组-->模型数组)
NSMutableArray *arrM = [NSMutableArray array];
for (NSDictionary *dict in array) {
[arrM addObject:[XMGApp appWithDict:dict]];
}
_apps = arrM;
}
return _apps;
}
#pragma mark -----------------------
#pragma mark UITableViewDataSource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.apps.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//01 创建cell
static NSString *ID = @"app";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
//02 设置cell
//02.01 得到该行cell对应的数据
XMGApp *mode = self.apps[indexPath.row];
//02.02 设置标题
cell.textLabel.text = mode.name;
//02.03 设置子标题
cell.detailTextLabel.text = mode.download;
//02.04 设置图片
//思路:先判断该图片是否已经下载过了(保存),如果已经下载过了那么就直接使用,否则再去下载
//二级缓存结构:
//钱 -- 身上(内存) + 账户(磁盘)
/*
(1)在显示图片前,先检查是否有内存缓存
(2)有内存缓存,直接使用
(3)如果没有内存缓存,再去检查是否有磁盘缓存
(4)有磁盘缓存,直接使用 + 保存一份到内存中(以后使用方便)
(5)没有磁盘缓存,下载图片并显示出来 + 保存一份到内存中+保存到磁盘
*/
//尝试去字典中取图片(存在|不存在)
UIImage *image = [self.images objectForKey:mode.icon];
if(image){
cell.imageView.image = image;
NSLog(@"第%zd行cell对应的图片使用了内存缓存",indexPath.row);
}else{
//获得文件的名称
NSString *fileName = [mode.icon lastPathComponent];//得到该路径的最后一个节点
//获得cache路径
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//拼接全路径
NSString *fullPath = [cache stringByAppendingPathComponent:fileName];
//判断是否有磁盘缓存-尝试去缓存路径取
NSData *data = [NSData dataWithContentsOfFile:fullPath];
if (data) {
//01 直接使用
UIImage *image = [UIImage imageWithData:data];
cell.imageView.image = image;
//02 保存一份到内存中
[self.images setObject:image forKey:mode.icon];
NSLog(@"第%zd行cell对应的图片使用了磁盘缓存",indexPath.row);
}else
{
//01 下载图片,并显示
NSURL *url = [NSURL URLWithString:mode.icon];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
cell.imageView.image = image;
//02 保存一份到内存中
[self.images setObject:image forKey:mode.icon];
//03 保存一份到磁盘中
[imageData writeToFile:fullPath atomically:YES];
NSLog(@"下载第%zd行cell对应的图片",indexPath.row);
}
}
//03 返回cell
return cell;
}
/*
问题:
UI不流畅 - 图片的下载操作在主线程中处理 ==> 在子线程中下载图片
重复下载:内存缓存->优化(磁盘缓存)
*/
/*
Doc:不允许
Lib
cache :图片缓存
偏好设置
tmp:临时存储
*/
@end
------------------------优化版----------------------
#import "ViewController.h"
#import "XMGApp.h"
@interface ViewController ()
@property (nonatomic, strong) NSArray *apps;
@property (nonatomic, strong) NSMutableDictionary *images;
@property (nonatomic, strong) NSMutableDictionary *operatios;
@property (nonatomic, strong) NSOperationQueue *queue;
@end
@implementation ViewController
-(NSMutableDictionary *)images
{
if (_images == nil) {
_images = [NSMutableDictionary dictionary];
}
return _images;
}
-(NSMutableDictionary *)operatios
{
if (_operatios == nil) {
_operatios = [NSMutableDictionary dictionary];
}
return _operatios;
}
-(NSArray *)apps
{
if (_apps == nil) {
//加载数据(字典数组)
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]];
//字典转模型(字典数组-->模型数组)
NSMutableArray *arrM = [NSMutableArray array];
for (NSDictionary *dict in array) {
[arrM addObject:[XMGApp appWithDict:dict]];
}
_apps = arrM;
}
return _apps;
}
-(NSOperationQueue *)queue
{
if (_queue == nil) {
_queue = [[NSOperationQueue alloc]init];
}
return _queue;
}
#pragma mark -----------------------
#pragma mark UITableViewDataSource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.apps.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//01 创建cell
static NSString *ID = @"app";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
//02 设置cell
//02.01 得到该行cell对应的数据
XMGApp *mode = self.apps[indexPath.row];
//02.02 设置标题
cell.textLabel.text = mode.name;
//02.03 设置子标题
cell.detailTextLabel.text = mode.download;
//02.04 设置图片
//思路:先判断该图片是否已经下载过了(保存),如果已经下载过了那么就直接使用,否则再去下载
//二级缓存结构:钱 -- 身上(内存) + 账户(磁盘)
/*
(1)在显示图片前,先检查是否有内存缓存
(2)有内存缓存,直接使用
(3)如果没有内存缓存,再去检查是否有磁盘缓存
(4)有磁盘缓存,直接使用 + 保存一份到内存中(以后使用方便)
(5)没有磁盘缓存,下载图片并显示出来 + 保存一份到内存中+保存到磁盘
*/
//尝试去字典中取图片(存在|不存在)
UIImage *image = [self.images objectForKey:mode.icon];
if(image){
cell.imageView.image = image;
NSLog(@"第%d行cell对应的图片使用了内存缓存",indexPath.row);
}else{
//获得文件的名称
NSString *fileName = [mode.icon lastPathComponent];//得到该路径的最后一个节点
//获得cache路径
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//拼接全路径
NSString *fullPath = [cache stringByAppendingPathComponent:fileName];
//判断是否有磁盘缓存-尝试去缓存路径取
NSData *data = [NSData dataWithContentsOfFile:fullPath];
data = nil;
if (data) {
//01 直接使用
UIImage *image = [UIImage imageWithData:data];
cell.imageView.image = image;
//02 保存一份到内存中
[self.images setObject:image forKey:mode.icon];
NSLog(@"第%zd行cell对应的图片使用了磁盘缓存",indexPath.row);
}else
{
//解决图片错乱问题:01
//cell.imageView.image = nil;
//解决图片错乱问题:02 设置默认的占位图
cell.imageView.image = [UIImage imageNamed:@"Snip20161121_67"];
//001 创建队列(自定义)-抽取成属性
//对图片下载的操作进行缓存
//如果图片缓存不存在,那么先判断该图片的下载操作是否已经存在了,如果已经开始下载那么只需等待
//如果该图片没有在下载,那么再封装操作
//先检查图片是否正在下载(检查操作缓存)
NSBlockOperation *download = [self.operatios objectForKey:mode.icon];
if(download)
{
//该图片正在下载
NSLog(@"第%zd行cell对应的图片正在下载,请稍等",indexPath.row);
}else
{
//002 封装操作
NSBlockOperation *download = [NSBlockOperation blockOperationWithBlock:^{
//01 下载图片,并显示
NSURL *url = [NSURL URLWithString:mode.icon];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
//耗时操作:演示网速慢的情况
for (int i = 0; i < 1000000000; ++i) {
}
//02 保存一份到内存中
[self.images setObject:image forKey:mode.icon];
//03 保存一份到磁盘中
[imageData writeToFile:fullPath atomically:YES];
NSLog(@"下载第%zd行cell对应的图片",indexPath.row);
//线程间通信(主线程显示图片)
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//cell.imageView.image = image;
//重新刷新cell
//[tableView reloadData];//刷新整个tableView
//刷新指定的行 该方法内部会重新调用cellForRowAtIndexPath方法
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];
}];
}];
// 把操作保存一份
[self.operatios setObject:download forKey:mode.icon];
//003 把操作添加到队列
[self.queue addOperation:download];
}
}
}
//03 返回cell
return cell;
}
/*
问题:
UI不流畅 - 图片的下载操作在主线程中处理 ==> 在子线程中下载图片
图片不显示:
图片显示错乱:
重复下载:内存缓存->优化(磁盘缓存)
*/
/*
Doc:不允许
Lib
cache :图片缓存
偏好设置
tmp:临时存储
*/
@end
二、网络
基本概念 服务器1、http协议
- URL
-
POST和GET
2、NSURLConnection
image.png2.1 、NSURLConnection发送GET请求
-
NSURLConnection发送同步请求(GET)
NSURLConnection发送同步请求(GET)
-
NSURLConnection发送异步请求01(GET)
NSURLConnection发送异步请求(GET)
NSURLConnection发送异步请求02(GET)
------代理方法-------
#import "ViewController.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
@property(nonatomic,strong)NSMutableData *fileData;
@end
@implementation ViewController
-(NSMutableData *)fileData{
if (_fileData == nil) {
_fileData = [NSMutableData data];
}
return _fileData;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self sendAsynDelegate];
}
//通过代理NSURLConnection的异步请求
-(void)sendAsynDelegate{
//01 确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520&type=JSON"];
//02 设置请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 设置代理 马上发送请求
// NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:request delegate:self];
//设置代理 startImmediately:YES 表示马上开始 NO 暂不发送请求
NSURLConnection *connect = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
//控制网络请求 取消
// [connect cancel];
//手动调用该方法发送请求
// [connect start];
}
#pragma mark------NSURLConnectionDataDelegate
//01 接收到服务器的响应
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(@"didReceiveResponse");
}
//02 接收服务器返回的数据 此方法会调用多次,数据是一点一点传输的
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
//追加接收的数据
[self.fileData appendData:data];
NSLog(@"didReceiveData");
}
//03 请求完成时调用
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
//解析服务器返回的数据
NSLog(@"%@",[[NSString alloc] initWithData:self.fileData encoding:NSUTF8StringEncoding]);
NSLog(@"connectionDidFinishLoading");
}
//请求失败的时候调用
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(@"didFailWithError");
}
@end
2.2 、NSURLConnection发送POST请求
NSURLConnection发送POST请求 转码操作总结:
3、NSRULSession:
NSURLSession简单介绍 获取session的方法 常用的方法3.1、NSURLSession的基本使用(NSURLSessionDataTask)
- GET请求
- POST请求
- NSURLSession代理方法 GET请求
#import "ViewController.h"
@interface ViewController ()<NSURLSessionDataDelegate>
@property (nonatomic, strong) NSMutableData *resultData;
@end
@implementation ViewController
-(NSMutableData *)resultData
{
if (_resultData == nil) {
_resultData = [NSMutableData data];
}
return _resultData;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self delegate];
}
-(void)delegate
{
//(1)确定请求路径(URL)
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//(2)创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//(3)自定义会话对象session,设置代理
/* 参数说明
*
* 第一个参数:配置信息(设置请求的,功能类似于NSURLRequest) defaultSessionConfiguration默认
* 第二个参数:设置代理
* 第三个参数:代理队列(线程)-决定代理方法在哪个线程中调用
* [NSOperationQueue mainQueue] 主线程中执行
* [[NSOperationQueue alloc]init] 子线程
* nil 默认在子线程中执行
*/
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
//(4)根据会话对象来创建请求任务(Task)
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
//(5)执行Task(发送请求)
[dataTask resume];
}
#pragma mark -----------------------
#pragma mark NSURLSessionDataDelegate
//01 接收到服务器响应的时候会调用
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
NSLog(@"didReceiveResponse--%@",[NSThread currentThread]);
//需要通过调用completionHandler,告诉系统应该如何处理服务器返回的数据
completionHandler(NSURLSessionResponseAllow); //告诉服务器接收返回的数据
}
//02 接收到服务器返回数据的时候调用 该方法可能会被调用多次
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
NSLog(@"didReceiveData");
//拼接服务器返回的数据
[self.resultData appendData:data];
}
//03 请求完成或者是失败的时候调用 通过判断error是否有值来判断是否请求失败
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError");
//解析数据
NSLog(@"%@",[[NSString alloc]initWithData:self.resultData encoding:NSUTF8StringEncoding]);
}
@end
下载文件,使用文件句柄解决内存飙升问题:
--------------------------大文件下载利用NSURLSessionDataTask代理方法,监听下载进度,解决内存飙升问题---------------------------
#import "ViewController.h"
@interface ViewController ()<NSURLSessionDataDelegate>
@property (nonatomic, assign) NSInteger totalSize; //文件的总大小
@property (nonatomic, assign) NSInteger currentSize; //当前已经下载的数据大小
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@property (nonatomic, strong) NSFileHandle *handle;
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//01 确定资源路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 创建会话对象 设置代理
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//04 创建下载请求Task
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
//05 发送请求
[dataTask resume];
}
#pragma mark -----------------------
#pragma mark NSURLSessionDataDelegate
//01 接收到响应的时候调用
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
//得到本次请求的文件数据大小
self.totalSize = response.expectedContentLength;
//0 拼接获取文件的存储路径
//得到文件的名称:得到请求的响应头信息,获取响应头信息中推荐的文件名称
NSString *fileName = [response suggestedFilename];
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//拼接文件的存储路径(沙盒路径cache) + 文件名
NSString *fullPath = [cache stringByAppendingPathComponent:fileName];
//(1)创建空的文件
[[NSFileManager defaultManager] createFileAtPath:fullPath contents:nil attributes:nil];
//(2)创建文件句柄指针指向该文件
self.handle = [NSFileHandle fileHandleForWritingAtPath:fullPath];
//告诉系统应该接收数据
completionHandler(NSURLSessionResponseAllow);
}
//02 接收到服务器返回数据的时候调用 可能会调用多次
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
//(3)当接收到数据的时候,使用该句柄来写数据
[self.handle writeData:data];
//计算进度
self.currentSize +=data.length;
NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
self.progressView.progress = self.currentSize / self.totalSize;
}
//03 下载完成或者是失败的时候调用
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
// (4)当所有的数据写完,应该关闭句柄指针
[self.handle closeFile];
}
//文件句柄(指针) NSFileHandle实现边接收边写数据到沙盒中
/*
特点:在写数据的时候边写数据边移动位置
使用步骤:
(1)创建空的文件
(2)创建文件句柄指针指向该文件
(3)当接收到数据的时候,使用该句柄来写数据
(4)当所有的数据写完,应该关闭句柄指针
*/
@end
断点下载和离线断点下载
NSURLSessionDataTask的断点下载(开始-暂停-回复-取消下载)+离线断点下线(断点续传)
- 1、利用文件句柄的方式实现边接收边写数据
#import "ViewController.h"
#define KfullPath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"xmg.mp4"]
#define KSizefullPath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"xmg.xmg"]
@interface ViewController ()<NSURLSessionDataDelegate>
@property (nonatomic, assign) NSInteger totalSize; //文件的总大小
@property (nonatomic, assign) NSInteger currentSize; //当前已经下载的数据大小
@property (nonatomic, strong) NSFileHandle *handle;
@property (nonatomic, strong)NSURLSessionDataTask *dataTask;
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@property (nonatomic,strong) NSURLSession *session;
@end
@implementation ViewController
#pragma mark -----------------------
#pragma mark life Cycle
-(void)viewDidLoad
{
[super viewDidLoad];
//拿到之前已经下载的文件数据大小 self.currentSize = 沙盒中文件的大小
//01 得到沙盒中已经下载的文件的属性
NSDictionary *fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath:KfullPath error:nil];
NSLog(@"%@",fileInfo);
self.currentSize = [fileInfo fileSize]; //得到沙盒中已经下载文件的总大小
//处理进度信息 = 已经下载大小/文件的总大小
//尝试读取沙盒中xmg.xmg文件的信息 (文件的总大小)
NSData *sizeData = [NSData dataWithContentsOfFile:KSizefullPath];
self.totalSize = [[[NSString alloc]initWithData:sizeData encoding:NSUTF8StringEncoding] integerValue];
if (self.totalSize !=0) {
NSLog(@"%f",1.0 * self.currentSize /self.totalSize);
self.progressView.progress = 1.0 * self.currentSize /self.totalSize;
}
}
#pragma mark -----------------------
#pragma mark lazy loading
-(NSURLSession *)session
{
if (_session == nil) {
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
-(NSURLSessionDataTask *)dataTask
{
if (_dataTask == nil) {
//01 确定资源路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
//02 创建请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//+ 设置请求头信息(告诉请求对象只下载某一部分数据)Range
/*
Range:
bytes=0-100
bytes=-100 文件开始-100
bytes=400-1000
bytes=400- 400个字节的位置开始一直到文件的结尾
*/
NSString *header = [NSString stringWithFormat:@"bytes=%zd-",self.currentSize];
[request setValue:header forHTTPHeaderField:@"Range"];
NSLog(@"请求下载的数据范围为:%@",header);
//03 创建会话对象 设置代理
//04 创建下载请求Task
_dataTask = [self.session dataTaskWithRequest:request];
}
return _dataTask;
}
#pragma mark -----------------------
#pragma mark btnClick
- (IBAction)startBtnClick:(id)sender
{
[self.dataTask resume];
}
- (IBAction)suspendBtnClick:(id)sender
{
//暂停下载 可以恢复下载
[self.dataTask suspend];
NSLog(@"暂停文件的下载任务......");
}
- (IBAction)cancelBtnClick:(id)sender
{
//取消下载 是不可以(不支持)恢复下载,点击取消之后dataTask就已经结束了
[self.dataTask cancel];
NSLog(@"文件的下载操作被取消.....");
//手动清空dataTask
self.dataTask = nil;
}
- (IBAction)resumeBtnClick:(id)sender
{
//恢复下载
[self.dataTask resume];
}
#pragma mark -----------------------
#pragma mark NSURLSessionDataDelegate
//01 接收到响应的时候调用 每发送一次请求就会调用一次该方法
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
/*
9071810 9M
1322052-文件的末尾
7749758 + 1322052 = 9071810
*/
//得到本次请求的文件数据大小 文件的总大小 = 本次请求的数据大小 + 已经下载的文件数据大小
self.totalSize = response.expectedContentLength + self.currentSize;
NSLog(@"接收到服务器的响应---%zd-----",self.totalSize);
//把文件的总大小信息保存起来(写入到沙盒中指定的文件)
NSData *sizeData = [[NSString stringWithFormat:@"%zd",self.totalSize] dataUsingEncoding:NSUTF8StringEncoding];
[sizeData writeToFile:KSizefullPath atomically:YES];
//判断:判断是否是第一次发送请求下载(只有在第一次下载的时候才需要来创建文件)
if (self.currentSize == 0) {
//(1)创建空的文件
[[NSFileManager defaultManager] createFileAtPath:KfullPath contents:nil attributes:nil];
}
//(2)创建文件句柄指针指向该文件 默认指向文件的开头
self.handle = [NSFileHandle fileHandleForWritingAtPath:KfullPath];
//调整:移动文件句柄指针指向文件的末尾
[self.handle seekToEndOfFile];
//告诉系统应该接收数据
completionHandler(NSURLSessionResponseAllow);
}
//02 接收到服务器返回数据的时候调用 可能会调用多次
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
//(3)当接收到数据的时候,使用该句柄来写数据
[self.handle writeData:data];
//计算进度
self.currentSize +=data.length;
NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
self.progressView.progress = 1.0 *self.currentSize / self.totalSize;
}
//03 请求完成或者是失败的时候调用
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
// (4)当所有的数据写完,应该关闭句柄指针
[self.handle closeFile];
NSLog(@"didCompleteWithError--%@",error);
}
//文件句柄(指针) NSFileHandle实现边接收边写数据到沙盒中
/*
特点:在写数据的时候边写数据边移动位置
使用步骤:
(1)创建空的文件
(2)创建文件句柄指针指向该文件
(3)当接收到数据的时候,使用该句柄来写数据
(4)当所有的数据写完,应该关闭句柄指针
*/
@end
- 2、利用输出流(NSOutputStream)实现边接收边写数据
//01 接收到响应的时候调用 每发送一次请求就会调用一次该方法
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
/*
9071810 9M
1322052-文件的末尾
7749758 + 1322052 = 9071810
*/
//得到本次请求的文件数据大小 文件的总大小 = 本次请求的数据大小 + 已经下载的文件数据大小
self.totalSize = response.expectedContentLength + self.currentSize;
NSLog(@"接收到服务器的响应---%zd-----",self.totalSize);
//把文件的总大小信息保存起来(写入到沙盒中指定的文件)
NSData *sizeData = [[NSString stringWithFormat:@"%zd",self.totalSize] dataUsingEncoding:NSUTF8StringEncoding];
[sizeData writeToFile:KSizefullPath atomically:YES];
// (1)创建输出流(指向文件),打开输出流
/* 参数说明
*
* 第一个参数:指向的文件路径 如果指定路径的文件不存在那么输出流会自动的创建一个空的文件
* 第二个参数:表示是否要进行数据追加
*/
NSOutputStream *stream = [[NSOutputStream alloc]initToFileAtPath:KfullPath append:YES];
[stream open];
self.stream = stream;
//告诉系统应该接收数据
completionHandler(NSURLSessionResponseAllow);
}
//02 接收到服务器返回数据的时候调用 可能会调用多次
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
// (2)当接收到服务器返回数据的时候,使用输出流来写数据
[self.stream write:data.bytes maxLength:data.length];
//计算进度
self.currentSize +=data.length;
NSLog(@"%f",1.0 * self.currentSize / self.totalSize);
self.progressView.progress = 1.0 *self.currentSize / self.totalSize;
}
//03 请求完成或者是失败的时候调用
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
// (3)当所有的数据写完,应该关闭输出流
[self.stream close];
NSLog(@"didCompleteWithError--%@",error);
}
//文件句柄(指针) NSFileHandle实现边接收边写数据到沙盒中
/*
特点:在写数据的时候边写数据边移动位置
使用步骤:
(1)创建空的文件
(2)创建文件句柄指针指向该文件
(3)当接收到数据的时候,使用该句柄来写数据
(4)当所有的数据写完,应该关闭句柄指针
*/
//输出流
/*
(1)创建输出流(指向文件),打开输出流
(2)当接收到服务器返回数据的时候,使用输出流来写数据
(3)当所有的数据写完,应该关闭输出流
*/
3.2、NSURLSession的基本使用(NSURLSessionDownLoadTask)
总结NSURLSessionDownLoadTask下载的两种方法
- 方法1
//缺点:无法监听文件的进度
-(void)downloadBlock
{
//01 确定资源路径
NSURL *url = [NSURL URLWithString:@"http://img4q.duitang.com/uploads/item/201406/09/20140609150919_ZztLd.jpeg"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 创建会话对象
NSURLSession *session = [NSURLSession sharedSession];
//04 创建downloadTask
/* 参数说明
*
* 第一个参数:请求对象
* 第二个参数:completionHandler 请求完成(成功|失败)的时候调用
* location:位置 文件的位置 内部已经实现了边接收数据边写沙盒的操作
* response:响应头信息
*/
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//默认已经把数据写到磁盘中:tmp/...随时可能被删除
NSLog(@"%@",location);
//转移文件(转移到安全的地方去)
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *fullPath = [cachePath stringByAppendingPathComponent:response.suggestedFilename];
//路径 ->NSURL
//URLWithString 不做其他额外的处理
//fileURLWithPath
NSLog(@"%@",[NSURL URLWithString:fullPath]);
NSLog(@"%@",[NSURL fileURLWithPath:fullPath]);
/*
/Users/xiaomage/Library/Developer/CoreSimulator/Devices/E1970026-0911-4EA8-BD41-3B700072A2B7/data/Containers/Data/Applic ... tLd.jpeg
file:///Users/xiaomage/Library/Developer/CoreSimulator/Devices/E1970026-0911-4EA8-BD41-3B700072A2B7/data/Containers/Data/Application/DB7ED1AC-97BC-4EA8-AD5F-C250799D53EF/Library/Caches/20140609150919_ZztLd.jpe
*/
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
}];
//05 发送请求
[downloadTask resume];
}
- 方法2 代理方法(断点下载)
#import "ViewController.h"
@interface ViewController ()<NSURLSessionDownloadDelegate>
@property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;
@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, strong) NSData *fileInfoData;
@end
@implementation ViewController
#pragma mark ----------------
#pragma mark lazy loading
-(NSURLSession *)session
{
if (_session== nil) {
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
-(NSURLSessionDownloadTask *)downloadTask
{
if (_downloadTask == nil) {
//01 确定资源路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 创建会话对象 设置代理
//04 创建downloadTask
_downloadTask =[self.session downloadTaskWithRequest:request];
}
return _downloadTask;
}
#pragma mark ----------------
#pragma mark BtnClick
- (IBAction)startBtnClick:(id)sender
{
[self.downloadTask resume];
}
- (IBAction)suspendBtnClick:(id)sender
{
//暂停
[self.downloadTask suspend];
NSLog(@"暂停下载任务++++");
}
- (IBAction)cacnelBtnClick:(id)sender
{
//取消 普通的取消操作是不可以恢复的
//[self.downloadTask cancel];
//取消,可以恢复的取消操作
//resumeData 可以用来恢复下载的数据 并不是沙盒中保存的已经下载好的文件数据
[self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
self.fileInfoData = resumeData;
}];
self.downloadTask = nil;
NSLog(@"取消下载任务+++");
}
- (IBAction)resumeBtnClick:(id)sender
{
//暂停->恢复
if(self.fileInfoData)
{
//取消->恢复
//在恢复下载的时候,判断是否有可以用来进行恢复下载的数据,如果有那么就根据该数据创建一个新的网络请求
self.downloadTask = [self.session downloadTaskWithResumeData:self.fileInfoData];
self.fileInfoData = nil;
}
[self.downloadTask resume];
}
#pragma mark ----------------
#pragma mark NSURLSessionDownloadDelegate
//01 写数据的时候调用
// bytesWritten 本次写入的数据大小
// totalBytesWritten 写入数据的总大小
// totalBytesExpectedToWrite 文件的总大小
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
NSLog(@"%f",1.0 * totalBytesWritten / totalBytesExpectedToWrite);
}
//02 下载完成的时候调用
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
//转移文件(转移到安全的地方去)
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *fullPath = [cachePath stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
//剪切文件
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
NSLog(@"%@",fullPath);
}
//03 整个请求结束或者是失败的时候调用
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError---%@",error);
}
#pragma mark ----------------
#pragma mark NSURLSessionDownloadTask下载文件的两种方法给
//缺点:无法监听文件的进度
-(void)downloadBlock
{
//01 确定资源路径
NSURL *url = [NSURL URLWithString:@"http://img4q.duitang.com/uploads/item/201406/09/20140609150919_ZztLd.jpeg"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 创建会话对象
NSURLSession *session = [NSURLSession sharedSession];
//04 创建downloadTask
/* 参数说明
*
* 第一个参数:请求对象
* 第二个参数:completionHandler 请求完成(成功|失败)的时候调用
* location:位置 文件的位置 内部已经实现了边接收数据边写沙盒的操作
* response:响应头信息
*/
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//默认已经把数据写到磁盘中:tmp/...随时可能被删除
NSLog(@"%@",location);
//转移文件(转移到安全的地方去)
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *fullPath = [cachePath stringByAppendingPathComponent:response.suggestedFilename];
//路径 ->NSURL
//URLWithString 不做其他额外的处理
//fileURLWithPath
NSLog(@"%@",[NSURL URLWithString:fullPath]);
NSLog(@"%@",[NSURL fileURLWithPath:fullPath]);
/*
/Users/xiaomage/Library/Developer/CoreSimulator/Devices/E1970026-0911-4EA8-BD41-3B700072A2B7/data/Containers/Data/Applic ... tLd.jpeg
file:///Users/xiaomage/Library/Developer/CoreSimulator/Devices/E1970026-0911-4EA8-BD41-3B700072A2B7/data/Containers/Data/Application/DB7ED1AC-97BC-4EA8-AD5F-C250799D53EF/Library/Caches/20140609150919_ZztLd.jpe
*/
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
}];
//05 发送请求
[downloadTask resume];
}
-(void)downloadDelegate
{
//01 确定资源路径
NSURL *url = [NSURL URLWithString:@"http://img4q.duitang.com/uploads/item/201406/09/20140609150919_ZztLd.jpeg"];
//02 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//03 创建会话对象 设置代理
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//04 创建downloadTask
NSURLSessionDownloadTask *downloadTask =[session downloadTaskWithRequest:request];
//05 发送请求
[downloadTask resume];
}
@end
3.3、NSURLSession的基本使用(NSURLSessionUpLoadTask)
文件上传步骤 固定格式 要上传文件的二进制数据类型 MIMEType 组成:大类型/小类型获取MIMEType的几种方法
#import "ViewController.h"
#import <MobileCoreServices/MobileCoreServices.h>
@interface ViewController ()
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//01 搜索|http://www.w3school.com.cn/media/media_mimeref.asp
//02 发送请求
//03 调用C语言的api
//04 通用的二进制数据类型 application/octet-stream
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL fileURLWithPath:@"/Users/xiaomage/Desktop/Snip20161126_238.png"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//响应头信息:对服务器端已经对响应本身的描述
NSLog(@"%@",response.MIMEType);
}]resume];
NSLog(@"%@",[self mimeTypeForFileAtPath:@"/Users/xiaomage/Desktop/Snip20161126_238.png"]);
}
- (NSString *)mimeTypeForFileAtPath:(NSString *)path
{
if (![[[NSFileManager alloc] init] 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);
}
@end
NSURLSessionUploadTask
#import "ViewController.h"
#define Kboundary @"----WebKitFormBoundaryATJp9y6FGSNtJKNW"
#define KnewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
@interface ViewController ()<NSURLSessionDataDelegate>
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self upload2];
}
-(void)upload1
{
//(1)确定上传路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
//(2)创建"可变"请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//(3)修改请求方法为POST
request.HTTPMethod = @"POST";
//+ "设置请求头信息,告诉服务器这是一个文件上传请求"
//Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryATJp9y6FGSNtJKNW
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary] forHTTPHeaderField:@"Content-Type"];
//+ "按照固定的格式来拼接数据"
//(4)设置请求体信息(文件参数)
//(5)创建会话对象
NSURLSession *session = [NSURLSession sharedSession];
//(6)根据会话对象来创建uploadTask
/* 参数说明
*
* 第一个参数:请求对象
* 第二个参数:本应该放在请求体中的信息
*/
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:[self bodyData] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}];
//(7)执行task发送请求上传文件
[uploadTask resume];
}
-(void)upload2
{
//(1)确定上传路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
//(2)创建"可变"请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//(3)修改请求方法为POST
request.HTTPMethod = @"POST";
//+ "设置请求头信息,告诉服务器这是一个文件上传请求"
//Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryATJp9y6FGSNtJKNW
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary] forHTTPHeaderField:@"Content-Type"];
//+ "按照固定的格式来拼接数据"
//(4)设置请求体信息(文件参数)
//(5)创建会话对象 设置代理
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//(6)根据会话对象来创建uploadTask
/* 参数说明
*
* 第一个参数:请求对象
* 第二个参数:本应该放在请求体中的信息
*/
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:[self bodyData] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}];
//(7)执行task发送请求上传文件
[uploadTask resume];
}
-(NSData *)bodyData
{
NSMutableData *data = [NSMutableData data];
//01 拼接文件参数
/*
--分隔符
Content-Disposition: form-data; name="file"; filename="Snip20161126_210.png"
Content-Type: image/png
空行
文件数据
*/
[data appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
//name:file 服务器规定
//filename:该文件上传到服务器之后的名称
//username|pwd
[data appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"Snip20161126_210.png\"" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
//要上传文件的二进制数据类型 MIMEType 组成:大类型/小类型
[data appendData:[@"Content-Type: image/png" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
[data appendData:KnewLine];
NSData *imageData = [NSData dataWithContentsOfFile:@"/Users/apple/Desktop/Snip20161126_238.png"];
[data appendData:imageData];
[data appendData:KnewLine];
//02 拼接非文件参数
/*
--分隔符
Content-Disposition: form-data; name="username"
空行
abcdf
*/
[data appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
//name:username
[data appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
[data appendData:KnewLine];
[data appendData:[@"abcdef" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:KnewLine];
//03 结尾标识
/*
--分隔符--
*/
[data appendData:[[NSString stringWithFormat:@"--%@--",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
return data;
}
#pragma mark -----------------------
#pragma mark NSURLSessionDataDelegate
//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);
}
@end
NSURLSessionConfiguration
NSURLSessionConfiguration重要属性
文件解压缩
ZipArchive框架#import "ViewController.h"
#import "SSZipArchive.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self unzip];
}
//文件的压缩处理
-(void)zip
{
NSArray *arrayM = @[
@"/Users/xiaomage/Desktop/Snip20161126_235.png",
@"/Users/xiaomage/Desktop/Snip20161126_210.png",
@"/Users/xiaomage/Desktop/Snip20161126_238.png"
];
[SSZipArchive createZipFileAtPath:@"/Users/xiaomage/Desktop/test.zip" withFilesAtPaths:arrayM];
}
-(void)zip2
{
[SSZipArchive createZipFileAtPath:@"/Users/xiaomage/Desktop/ooo.zip" withContentsOfDirectory:@"/Users/xiaomage/Desktop/XXX"];
}
-(void)unzip
{
//第一个参数:zip文件的路径
//第二个参数:目标路径
[SSZipArchive unzipFileAtPath:@"/Users/xiaomage/Desktop/test.zip" toDestination:@"/Users/xiaomage/Desktop/XXXX"];
}
@end
总结:
网友评论