一、小文件下载,eg:图片
NSURL *url = [NSURL URLWithString:@"图片的地址XXX"];NSData *data = [NSData dataWithContentsOfURL:url];
[UIImage imageWithData:data]
例如:NSURLConnection发送一个异步的Get请求
response.suggestedFilename 这句代表获取下载的文件名
这里要用到NSFilehandle这个类,这个类可以实现对文件的读取、写入、更新。
下面总结了一些常用的NSFileHandle的方法,在这个表中,fh是一个NSFileHandle对象,data是一个NSData对象,path是一个NSString 对象,offset是易额Unsigned long long变量。
在接受到响应的时候就在沙盒中创建一个空的文件,然后每次接收到数据的时候就拼接到这个文件的最后面,通过- (unsigned long long)seekToEndOfFile; 这个方法
NSURLConnection 只提供了一个cancel方法,这并不是暂停,而是取消下载任务。如果要实现断点下载必须要了解HTTP协议中请求头的Range。
不难看出,通过设置请求头的Range我们可以指定下载的位置、大小。
那么我们这样设置bytes=500- 从500字节以后的所有字节,
只需要在didReceiveData中记录已经写入沙盒中文件的大小(self.currentLength),
把这个大小设置到请求头中,因为第一次下载肯定是没有执行过didReceive方法,self.currentLength也就为0,也就是从头开始下。
#pragma mark --按钮点击事件
- (IBAction)btnClicked:(UIButton *)sender {
// 状态取反
sender.selected = !sender.isSelected;
// 断点续传 // 断点下载
if (sender.selected) {
// 继续(开始)下载
// 1.URL
NSURL *url = [NSURL URLWithString:@"http://localhost:8080//term_app/hdgg.zip"];
// 2.请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 设置请求头
NSString *range = [NSString stringWithFormat:@"bytes=%lld-", self.currentLength];
[request setValue:range forHTTPHeaderField:@"Range"];
// 3.下载
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
} else {
// 暂停
[self.connection cancel];
self.connection = nil;
}
}
三、NSURLSession下载方式(与NSURLConnection不同的是,NSURLSession需要手动设置下载)使用NSURLSession就非常简单了,不需要去考虑什么边下载边写入沙盒的问题
NSURL* url = [NSURL URLWithString:@"http://dlsw.baidu.com/sw-search-sp/soft/9d/25765/sogou_mac_32c_V3.2.0.1437101586.dmg"];
// 得到session对象
NSURLSession* session = [NSURLSession sharedSession];
// 创建任务
NSURLSessionDownloadTask* downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
}];
// 开始任务
[downloadTask resume];
是不是跟NSURLConnection很像,但仔细看会发现回调的方法里面并没用NSData传回来,多了一个location,顾名思义,location就是下载好的文件写入沙盒的地址,打印一下发现下载好的文件被自动写入的temp文件夹下面了。
不过在下载完成之后会自动删除temp中的文件,所有我们需要做的只是在回调中把文件移动(或者复制,反正之后会自动删除)到caches中。
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
// response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致
NSString *file = [caches stringByAppendingPathComponent:response.suggestedFilename];
// 将临时文件剪切或者复制Caches文件夹
NSFileManager *mgr = [NSFileManager defaultManager];
// AtPath : 剪切前的文件路径
// ToPath : 剪切后的文件路径
[mgr moveItemAtPath:location.path toPath:file error:nil];
以上方法不能监听下载进度,实现NSURLSession的delegate方法就能监听下载进度
#pragma mark -- NSURLSessionDownloadDelegate
/** * 下载完毕会调用 * * @param location 文件临时地址 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTaskdidFinishDownloadingToURL:(NSURL *)location{
}
/** * 每次写入沙盒完毕调用 * 在这里面监听下载进度,totalBytesWritten/totalBytesExpectedToWrite * * @param bytesWritten 这次写入的大小 * @param totalBytesWritten 已经写入沙盒的大小 * @param totalBytesExpectedToWrite 文件总大小 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
self.pgLabel.text = [NSString stringWithFormat:@"下载进度:%f",(double)totalBytesWritten/totalBytesExpectedToWrite];
}
/** * 恢复下载后调用, */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffsetexpectedTotalBytes:(int64_t)expectedTotalBytes{
}
任务取消的方法
- (void)cancelByProducingResumeData:(void (^)(NSData *resumeData))completionHandler;
取消操作以后会调用一个Block,并传入一个resumeData,该参数包含了继续下载文件的位置信息。也就是说,当你下载了10M得文件数据,暂停了。那么你下次继续下载的时候是从第10M这个位置开始的,而不是从文件最开始的位置开始下载。因而为了保存这些信息,所以才定义了这个NSData类型的这个属性:resumeData。这个data只包含了url跟已经下载了多少数据,不会很大,不用担心内存问题。
另外,session还提供了通过resumeData来创建任务的方法
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
示例程序下载:
https://github.com/hongfenglt/HFDownLoad