一、小文件下载,eg:图片

NSURL *url = [NSURL URLWithString:@"图片的地址XXX"];NSData *data = [NSData dataWithContentsOfURL:url];

[UIImage imageWithData:data]

例如:NSURLConnection发送一个异步的Get请求



NSURL* url = [NSURL URLWithString:@"需要下载的地址XXXX"];[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {self.imageView.image = [UIImage imageWithData:data]; }];


上面的方法如果下载大文件会很消耗内存,所以尽量下载小文件




二、NSURLConnection下载大文件


// 发送请求去下载 (创建完conn对象后,会自动发起一个异步请求)


[NSURLConnection connectionWithRequest:request delegate:self];


实现以下代理方法


@property (weak, nonatomic) IBOutlet UIProgressView *myPregress;


@property (nonatomic,strong) NSMutableData* fileData;/** * 文件的总长度 */


@property (nonatomic, assign) long long totalLength;




/** * 1.接收到服务器的响应就会调用 * * 


@param response 响应 */




- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{  


self.fileData = [NSMutableData data];  


// 获取要下载的文件的大小  


self.totalLength = response.expectedContentLength;


}




/** *   2.当接收到服务器返回的实体数据时调用(具体内容,这个方法可能会被调用多次) 


* * @param data 这次返回的数据 */




- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{  


[self.fileData appendData:data];  


self.myPregress.progress = (double)self.fileData.length / self.totalLength;


}




/** * 3.加载完毕后调用(服务器的数据已经完全返回后) */




- (void)connectionDidFinishLoading:(NSURLConnection *)connection{  


// 拼接文件路径  


NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];  


NSString *file = [cache stringByAppendingPathComponent:response.suggestedFilename];  


// 写到沙盒中  


[self.fileData writeToFile:file atomically:YES];




}


response.expectedContentLength 这句代码下载的文件的大小  。
response.suggestedFilename 这句代表获取下载的文件名


用来接受文件的NSMutableData一直都在内存中,会随着文件的下载一直变大,


合理的方式在我们获取一部分data的时候就写入沙盒中,然后释放内存中的data。


这里要用到NSFilehandle这个类,这个类可以实现对文件的读取、写入、更新。
下面总结了一些常用的NSFileHandle的方法,在这个表中,fh是一个NSFileHandle对象,data是一个NSData对象,path是一个NSString 对象,offset是易额Unsigned long long变量。


ios下载链接怎么用 ios怎么下载链接文件_ios下载链接怎么用


在接受到响应的时候就在沙盒中创建一个空的文件,然后每次接收到数据的时候就拼接到这个文件的最后面,通过- (unsigned long long)seekToEndOfFile; 这个方法




- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{  


// 文件路径  


NSString* ceches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];  


NSString* filepath = [ceches stringByAppendingPathComponent:response.suggestedFilename];  


// 创建一个空的文件到沙盒中  


NSFileManager* mgr = [NSFileManager defaultManager];  


[mgr createFileAtPath:filepath contents:nil attributes:nil];  


// 创建一个用来写数据的文件句柄对象  


self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:filepath];  


// 获得文件的总大小  


self.totalLength = response.expectedContentLength;}




/** * 2.当接收到服务器返回的实体数据时调用(具体内容,这个方法可能会被调用多次) * * @param data 这次返回的数据 */




- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{  


// 移动到文件的最后面  


[self.writeHandle seekToEndOfFile];  


// 将数据写入沙盒  


[self.writeHandle writeData:data];  


// 累计写入文件的长度  


self.currentLength += data.length;  


// 下载进度  


self.myPregress.progress = (double)self.currentLength / self.totalLength;}




/** * 3.加载完毕后调用(服务器的数据已经完全返回后) */




- (void)connectionDidFinishLoading:(NSURLConnection *)connection{  


self.currentLength = 0; self.totalLength = 0;  


// 关闭文件  


[self.writeHandle closeFile]; self.writeHandle = nil;


}


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