小文件的下载,代码示例:

 //NSURLSession的下载
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://gss0.baidu.com/-fo3dSag_xI4khGko9WTAnF6hhy/lvpics/w=600/sign=1350023d79899e51788e391472a5d990/b21bb051f819861810d03e4448ed2e738ad4e65f.jpg"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//回到主线程刷新界面
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = [UIImage imageWithData:data];
}); }] resume] ;

大文件的下载,代码示例:

#import "ViewController.h"

@interface ViewController ()<NSURLSessionDataDelegate>

@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@property (nonatomic,strong) NSMutableData * fileData ;
@property (nonatomic,assign) NSInteger totalSize ;
@property (nonatomic,assign) NSInteger currentSize ;
@property (weak, nonatomic) IBOutlet UIProgressView *progressView; @end @implementation ViewController- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //1 确定资源路径
NSURL *url = [NSURL URLWithString:@"http://meiye-mbs.oss-cn-shenzhen.aliyuncs.com/mbsFiles/0e3d0e4a0d5d4da5963e9e7617e8de101565841097849.mp4"];
//2 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3 创建会话对象:线程不传,默认在子线程中处理
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//4 创建下载请求Task
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
//5 发送请求
[dataTask resume];
} #pragma mark - 代理方法
//1 接收到响应的时候调用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { //得到本次请求的文件数据大小
self.totalSize = response.expectedContentLength; //告诉系统应该接收数据
completionHandler(NSURLSessionResponseAllow);
} //2 接收到服务器返回数据的时候调用,可能会调用多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
//拼接数据
[self.fileData appendData:data]; //计算文件的下载进度并显示= 已经下载的数据/文件的总大小
self.currentSize += data.length;
CGFloat progress = 1.0 * self.currentSize / self.totalSize;
self.progressView.progress = progress;
NSLog(@"%f", progress);
} //3 下载完成或者失败的时候调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { //得到文件的名称:得到请求的响应头信息,获取响应头信息中推荐的文件名称
NSString *fileName = [task.response suggestedFilename];
//拼接文件的存储路径(沙盒路径Cache + 文件名)
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//下载的路径
NSString *fullPath = [cache stringByAppendingPathComponent:fileName]; //下载好的数据写入到磁盘
[self.fileData writeToFile:fullPath atomically:YES];
NSLog(@"filePath = %@", fullPath);
} - (NSMutableData *)fileData {
if (!_fileData) {
_fileData = [NSMutableData data];
}
return _fileData;
}

如果按照上面那段代码的话,可以实现功能,但是有一个问题,就是 内存会飚升。为了处理这个问题,直接把拿到的数据,就写入沙盒,

逻辑如下:

//文件句柄(指针)NSFileHandle
/**
特点:在写数据的时候边写数据边移动位置
使用步骤:
(1)创建空的文件
(2)创建文件句柄指针指向文件
(3)当接收到数据的时候,使用该句柄来写数据
(4)当所有的数据写完,应该关闭句柄指针
*/

修改如下:

#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 { //1 确定资源路径
NSURL *url = [NSURL URLWithString:@"http://meiye-mbs.oss-cn-shenzhen.aliyuncs.com/mbsFiles/0e3d0e4a0d5d4da5963e9e7617e8de101565841097849.mp4"];
//2 创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3 创建会话对象:线程不传,默认在子线程中处理
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//4 创建下载请求Task
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
//5 发送请求
[dataTask resume];
} #pragma mark - 代理方法
//1 接收到响应的时候调用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { //得到文件的名称:得到请求的响应头信息,获取响应头信息中推荐的文件名称
NSString *fileName = [response suggestedFilename];
//拼接文件的存储路径(沙盒路径Cache + 文件名)
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//下载的路径
NSString *fullPath = [cache stringByAppendingPathComponent:fileName]; //(1)创建空的文件
[[NSFileManager defaultManager] createFileAtPath:fullPath contents:nil attributes:nil];
//(2)创建文件句柄指针指向文件
self.handle = [NSFileHandle fileHandleForWritingAtPath:fullPath]; //得到本次请求的文件数据大小
self.totalSize = response.expectedContentLength; //告诉系统应该接收数据
completionHandler(NSURLSessionResponseAllow);
} //2 接收到服务器返回数据的时候调用,可能会调用多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
[self.handle writeData:data]; //计算文件的下载进度并显示= 已经下载的数据/文件的总大小
self.currentSize += data.length;
CGFloat progress = 1.0 * self.currentSize / self.totalSize;
self.progressView.progress = progress;
NSLog(@"%f", progress);
} //3 下载完成或者失败的时候调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { //(4)当所有的数据写完,应该关闭句柄指针
[self.handle closeFile];
}

按照上面的代码可以解决内存的问题,但是,下完后,会发现,文件打不开,因为文件被损坏了,而且查看文件的大小会发现,文件的内存变小了。

出现这个问题的原因是:

(1)重新下载的时候,会重新创建一个空的文件,把之前下载的内容覆盖了。

(2)当取消下载的时候,文件句柄会默认从文件的开头开始存储,这样就会覆盖之前的内容。

解决思路:

(1)判断是否是第一次下载,如果是第一次下载就创建一个空的文件夹

(2)让文件句柄指针指向文件的末尾

代码修改如下:

#import "ViewController.h"

@interface ViewController ()<NSURLSessionDataDelegate>

@property (nonatomic,strong) NSURLSessionDataTask *dataTask;

@property (nonatomic,assign) NSInteger  totalSize ;
@property (nonatomic,assign) NSInteger currentSize ;
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@property (nonatomic,strong) NSFileHandle * handle ; @end @implementation ViewController - (IBAction)startAction:(id)sender {
[self.dataTask resume];
} - (IBAction)suspendAction:(id)sender {
[self.dataTask suspend];
} - (IBAction)cancleAction:(id)sender {
[self.dataTask cancel];
self.dataTask = nil;
} - (IBAction)resumeAction:(id)sender {
[self.dataTask resume];
} #pragma mark - 代理方法
//1 接收到响应的时候调用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { //得到文件的名称:得到请求的响应头信息,获取响应头信息中推荐的文件名称
NSString *fileName = [response suggestedFilename];
//拼接文件的存储路径(沙盒路径Cache + 文件名)
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//下载的路径
NSString *fullPath = [cache stringByAppendingPathComponent:fileName]; //判断是否是第一次发送请求下载,只有在第一次请求下载的时候才需要创建文件
if (self.currentSize == ) {
//(1)创建空的文件
[[NSFileManager defaultManager] createFileAtPath:fullPath contents:nil attributes:nil];
}
//(2)创建文件句柄指针指向文件:默认指向文件的开头
self.handle = [NSFileHandle fileHandleForWritingAtPath:fullPath]; //(3)移动文件句柄指针指向文件的末尾
[self.handle seekToEndOfFile];
//得到本次请求的文件数据大小
self.totalSize = response.expectedContentLength + self.currentSize; //告诉系统应该接收数据
completionHandler(NSURLSessionResponseAllow);
} //2 接收到服务器返回数据的时候调用,可能会调用多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
[self.handle writeData:data]; //计算文件的下载进度并显示= 已经下载的数据/文件的总大小
self.currentSize += data.length;
CGFloat progress = 1.0 * self.currentSize / self.totalSize;
self.progressView.progress = progress;
NSLog(@"%f", progress);
} //3 下载完成或者失败的时候调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { //(4)当所有的数据写完,应该关闭句柄指针
[self.handle closeFile];
} #pragma mark - 懒加载
- (NSURLSessionDataTask *)dataTask {
if (!_dataTask) {
//1 确定资源路径
NSURL *url = [NSURL URLWithString:@"http://meiye-mbs.oss-cn-shenzhen.aliyuncs.com/mbsFiles/0e3d0e4a0d5d4da5963e9e7617e8de101565841097849.mp4"];
//2 创建请求对象
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"]; //3 创建会话对象:线程不传,默认在子线程中处理
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//4 创建下载请求Task
_dataTask = [session dataTaskWithRequest:request];
}
return _dataTask;
}

最后,总结文件下载的思路:

断点续传的思路:

启动的时候,判断该路径下的文件夹,是否有数据,如果有数据的话,就在原先的基础上继续下载。

代码如下:

#import "ViewController.h"

#define kFullPath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"ceshi.mp4"]
#define kSizeFullPath [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"ceshi.ceshi"] @interface ViewController ()<NSURLSessionDataDelegate> @property (nonatomic,strong) NSURLSessionDataTask *dataTask; @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)viewDidLoad {
[super viewDidLoad]; //拿到之前已经下载的文件数据大小 self.currentSize = 沙盒中文件的大小
NSDictionary *fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath:kFullPath error:nil];
NSLog(@"---%@---",fileInfo);
self.currentSize = [fileInfo fileSize]; //处理进度信息 == 已经下载的大小/文件的总大小
NSData *sizeData = [NSData dataWithContentsOfFile:kSizeFullPath];
self.totalSize = [[[NSString alloc] initWithData:sizeData encoding:NSUTF8StringEncoding] integerValue];
if (self.totalSize != ) {
NSLog(@"%f", 1.0 * self.currentSize / self.totalSize);
self.progressView.progress = 1.0 * self.currentSize / self.totalSize;
}
} - (IBAction)startAction:(id)sender {
[self.dataTask resume];
} - (IBAction)suspendAction:(id)sender {
[self.dataTask suspend];
} - (IBAction)cancleAction:(id)sender {
[self.dataTask cancel];
self.dataTask = nil;
} - (IBAction)resumeAction:(id)sender {
[self.dataTask resume];
} #pragma mark - 代理方法
//1 接收到响应的时候调用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { //判断是否是第一次发送请求下载,只有在第一次请求下载的时候才需要创建文件
if (self.currentSize == ) {
//(1)创建空的文件
[[NSFileManager defaultManager] createFileAtPath:kFullPath contents:nil attributes:nil];
} //(2)创建文件句柄指针指向文件:默认指向文件的开头
self.handle = [NSFileHandle fileHandleForWritingAtPath:kFullPath]; //(3)移动文件句柄指针指向文件的末尾
[self.handle seekToEndOfFile]; //得到本次请求的文件数据大小
self.totalSize = response.expectedContentLength + self.currentSize; //把文件的总大小信息保存起来
NSData *sizeData = [[NSString stringWithFormat:@"%zd", self.totalSize] dataUsingEncoding:NSUTF8StringEncoding];
[sizeData writeToFile:kSizeFullPath atomically:YES]; //告诉系统应该接收数据
completionHandler(NSURLSessionResponseAllow);
} //2 接收到服务器返回数据的时候调用,可能会调用多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
[self.handle writeData:data]; //计算文件的下载进度并显示= 已经下载的数据/文件的总大小
self.currentSize += data.length;
CGFloat progress = 1.0 * self.currentSize / self.totalSize;
self.progressView.progress = progress;
NSLog(@"%f", progress);
} //3 下载完成或者失败的时候调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { //(4)当所有的数据写完,应该关闭句柄指针
[self.handle closeFile];
} #pragma mark - 懒加载
- (NSURLSessionDataTask *)dataTask {
if (!_dataTask) {
//1 确定资源路径
NSURL *url = [NSURL URLWithString:@"http://meiye-mbs.oss-cn-shenzhen.aliyuncs.com/mbsFiles/0e3d0e4a0d5d4da5963e9e7617e8de101565841097849.mp4"];
//2 创建请求对象
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"]; //3 创建会话对象:线程不传,默认在子线程中处理
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//4 创建下载请求Task
_dataTask = [session dataTaskWithRequest:request];
}
return _dataTask;
}

NSURLSession的文件下载的更多相关文章

  1. NSURLSession详解

    导语 现在NSURLConnection在开发中会使用的越来越少,iOS9已经将NSURLConnection废弃,现在最低版本一般适配iOS7,所以也可以使用. NSURLConnection相对于 ...

  2. 网络热恋之NSURLSession

    // // ViewController.m // NSURLSession代理简介 #import "ViewController.h" @interface ViewContr ...

  3. IOS 网络浅析-(十 NSURLSession下载简介)

    之前本来打算在写两个篇幅,但是在这片开写的时候觉得还是写一个比较好,有利于理解.NSURLSession下载是通过NSURLSession下载代理实现的,上一片也介绍了代理,之所以没有介绍下载是因为, ...

  4. iOS - NetRequest 网络数据请求

    1.网络请求 1.1 网络通讯三要素 1.IP 地址(主机名): 网络中设备的唯一标示.不易记忆,可以用主机名(域名). 1) IP V4: 0~255.0~255.0~255.0~255 ,共有 2 ...

  5. 文件下载(NSURLConnection/NSURLSession)

    最基本的网络文件下载(使用原生的网络请求) #pragma mark - 小文件下载 // 方法一: NSData dataWithContentsOfURL - (void)downloadFile ...

  6. [OC] NSURLSession

    有的程序员老了,还没听过NSURLSession 有的程序员还嫩,没用过NSURLConnection 有的程序员很单纯,他只知道AFN. NSURLConnection在iOS9被宣布弃用,NSUR ...

  7. iOS开发之Alamofire源码解析前奏--NSURLSession全家桶

    今天博客的主题不是Alamofire, 而是iOS网络编程中经常使用的NSURLSession.如果你想看权威的NSURLSession的东西,那么就得去苹果官方的开发中心去看了,虽然是英文的,但是结 ...

  8. [swift]NSURLSession

    一.简单说明 在iOS9.0之后,以前使用的NSURLConnection过期,苹果推荐使用NSURLSession来替换NSURLConnection完成网路请求相关操作. NSURLSession ...

  9. iOS网络2——NSURLSession使用详解

    原文在此 一.整体介绍 NSURLSession在2013年随着iOS7的发布一起面世,苹果对它的定位是作为NSURLConnection的替代者,然后逐步将NSURLConnection退出历史舞台 ...

随机推荐

  1. 未能找到元数据文件**.dll解决办法

    解决方案里有很多项目.生成时提示100多个错误,都是未能找到元数据文件**.dll. 那就清理一下解决方案,一个一个来吧. 生成GateWay.Utilities项目时,虽然提示成功了,却发现bin/ ...

  2. Docker 运行一个Web应用

    使用 docker 构建一个 web 应用程序. 我们将在docker容器中运行一个 Python Flask 应用来运行一个web应用 参数说明: -d:让容器在后台运行. -P:将容器内部使用的网 ...

  3. python xlwt写入excel操作

    引用https://www.cnblogs.com/python-robot/p/9958352.html 安装 $ pip install xlwt 例子: import xlwt # 创建一个wo ...

  4. golang中的pflag示例

    现在晚上在家啃kubeadm的源码, 在啃源码前,pflag,viper,cobra这三件套好像是必须的, 那就先弄懂一下这三个套件的套路吧. 第一个,pflag. https://www.cnblo ...

  5. Day6 - Python基础6 模块shelve、xml、re、subprocess、pymysql

    本节目录: 1.shelve模块 2.xml模块 3.re模块 4.subprocess模块 5.logging模块 6.pymysql 1.shelve 模块 shelve模块是一个简单的k,v将内 ...

  6. 爬取沪深a股数据

    首先从东方财富网获取股票代码 再从网易财经下载股票历史数据 import requests import random from bs4 import BeautifulSoup as bs impo ...

  7. AcWing 44. 分行从上往下打印二叉树

    地址 https://www.acwing.com/problem/content/description/42/ 题目描述从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印到一行. ...

  8. testNG常用用法总结

    一.testNG介绍 TestNG是Java中的一个测试框架, 类似于JUnit 和NUnit,   功能都差不多, 只是功能更加强大,使用也更方便 Java中已经有一个JUnit的测试框架了.  T ...

  9. python中append的使用

    没有系统地学习过python,最近在append的使用上遇到了大问题,吃到了苦头 之前一直单纯地认为通过append把数添加到list中,不需要提前开空间,非常便利,但却没有意识到这个过程并不是值传递 ...

  10. Codeforces Round #603 (Div. 2) C. Everyone is a Winner! 二分

    C. Everyone is a Winner! On the well-known testing system MathForces, a draw of n rating units is ar ...