最近在做一个小项目的时候,发现使用NSURLSession或者AFNNetworking进行断点续传时诸多的不便,于是自己封装了一个类来实现断点续传,在程序重新启动时仍然可以继续下载(需自己调用方法),同时可以在同一时间多次调用该类方法。使用时请注意传入各参数的合理性,方法内部并没有对传入的参数进行修正
主要技术: NSURLConnection、block、NFFileHandle
1、首先,我提供一个类方法,供外界调用。 创建的类名为DownloadService
//
// DownloadService.h
// 11111
//
// Created by Liu Feng on 14-2-17.
// Copyright (c) 2014年 Liu Feng. All rights reserved.
// #import <Foundation/Foundation.h> typedef void (^DownloadServiceSuccess)(NSString *savePath);
typedef void (^DownloadServiceFailure)(NSError *error); @interface DownloadService : NSObject
/**
* 下载指定URL的资源到路径
*
* @param urlStr 网络资源路径
* @param toPath 本地存储文件夹
* @param capacity 缓存大小,单位为Mb
* @param success 成功时回传本地存储路径
* @param failure 失败时回调的错误原因
*/
+ (void)downLoadWithURL:(NSString *)urlStr toDirectory:(NSString *)toDirectory cacheCapacity:(NSUInteger)capacity success:(DownloadServiceSuccess)success failure:(DownloadServiceFailure)failure; @end
2、在.m中实现
//
// DownloadService.m
// 11111
//
// Created by Liu Feng on 14-2-17.
// Copyright (c) 2014年 Liu Feng. All rights reserved.
// #import "DownloadService.h" static DownloadService *_download;
static NSMutableDictionary *_dictPath;
static NSMutableDictionary *_dictBlock;
static NSMutableDictionary *_dictHandle;
static unsigned long long _cacheCapacity; // 缓存
static NSMutableData *_cacheData; typedef void (^myBlcok)(NSString *savePath, NSError *error); @interface DownloadService ()<NSURLConnectionDataDelegate> @end @implementation DownloadService + (void)initialize
{
_download = [[DownloadService alloc] init];
_dictPath = [NSMutableDictionary dictionary]; // 存储文件路径
_dictBlock = [NSMutableDictionary dictionary]; // 存储block
_dictHandle = [NSMutableDictionary dictionary]; // 存储NSFileHandle对象
_cacheData = [NSMutableData data]; // 存放缓存
} + (void)downLoadWithURL:(NSString *)urlStr toDirectory:(NSString *)toDirectory cacheCapacity:(NSInteger)capacity success:(DownloadServiceSuccess)success failure:(DownloadServiceFailure)failure{ // 1. 创建文件
NSString *fileName = [urlStr lastPathComponent];
NSString *filePath = [NSString stringWithFormat:@"%@/%@", toDirectory, fileName]; // 记录文件起始位置
unsigned long long from = ;
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]){ // 已经存在
from = [[NSData dataWithContentsOfFile:filePath] length];
}else{ // 不存在,直接创建
[[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
} // url
NSURL *url = [NSURL URLWithString:urlStr]; // 请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0f]; // 设置请求头文件
NSString *rangeValue = [NSString stringWithFormat:@"bytes=%llu-", from];
[request addValue:rangeValue forHTTPHeaderField:@"Range"]; // 创建连接
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:_download]; // 保存文章连接
_dictPath[connection.description] = filePath; // 保存block,用于回调
myBlcok block = ^(NSString *savePath, NSError *error){
if (error) {
if (failure) {
failure(error);
}
}else{
if (success) {
success(savePath);
}
}
};
_dictBlock[connection.description] = block; // 保存缓存大小
_cacheCapacity = capacity * * ; // 开始连接
[connection start];
}
/**
* 接收到服务器响应
*
* @param connection 哪一个连接
* @param response 响应对象
*/
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// 取出文章地址
NSString *filePath = _dictPath[connection.description]; // 打开文件准备输入
NSFileHandle *outFile = [NSFileHandle fileHandleForWritingAtPath:filePath]; // 保存文件操作对象
_dictHandle[connection.description] = outFile;
}
/**
* 开始接收数据
*
* @param connection 哪一个连接
* @param data 二进制数据
*/
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// 取出文件操作对象
NSFileHandle *outFile = _dictHandle[connection.description]; // 移动到文件结尾
[outFile seekToEndOfFile]; // 保存数据
[_cacheData appendData:data]; if (_cacheData.length >= _cacheCapacity) {
// 写入文件
[outFile writeData:data]; // 清空数据
[_cacheData setLength:];
}
}
/**
* 连接出错
*
* @param connection 哪一个连接出错
* @param error 错误信息
*/
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// 取出文件操作对象
NSFileHandle *outFile = _dictHandle[connection.description]; // 关闭文件操作
[outFile closeFile]; // 回调block
myBlcok block = _dictBlock[connection.description]; if (block) {
block(nil, error);
} // 移除字典中
[_dictHandle removeObjectForKey:connection.description];
[_dictPath removeObjectForKey:connection.debugDescription];
[_dictBlock removeObjectForKey:connection.description];
}
/**
* 结束加载
*
* @param connection 哪一个连接
*/
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// 取出文件操作对象
NSFileHandle *outFile = _dictHandle[connection.description]; // 关闭文件操作
[outFile closeFile]; // 取出路径
NSString *savePath = [_dictPath objectForKey:connection.description]; // 取出block
myBlcok block = _dictBlock[connection.description]; // 回调
if (block) {
block(savePath, nil);
} // 移除字典中
[_dictHandle removeObjectForKey:connection.description];
[_dictPath removeObjectForKey:connection.debugDescription];
[_dictBlock removeObjectForKey:connection.description];
} @end