Objective-C利用AFN实现图片下载,支持断点续传,显示下载进度
2016-01-05 11:07
627 查看
AFN导入就不多说了,建议用cocoapods,并且建议装个插件管理器,很方便。AFN介绍也不多说了,以后写个详细的,今天主要写断点续传那点事。
直接进入主题
新建一个类,继承自NSObject,我命名为DownLoadOperation
头文件重定义属性和方法如下
@property(nonatomic , strong) NSURL* url;
@property(nonatomic , copy) NSString* (^cachePath)(void);
@property(nonatomic , strong) AFHTTPRequestOperation* requestOperation;
@property(nonatomic , copy) void(^progressBlock)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead);
-(void)downloadWithUrl:(id)url
cachePath:(NSString* (^) (void))cacheBlock
progressBlock:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))progressBlock
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
接下来在.m中实现
-(void)downloadWithUrl:(id)url
cachePath:(NSString* (^) (void))cacheBlock
progressBlock:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))progressBlock
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
self.cachePath = cacheBlock;
//获取缓存的长度
long long cacheLength = [[self class] cacheFileWithPath:self.cachePath()];
//获取请求
NSMutableURLRequest* request = [[self class] requestWithUrl:url Range:cacheLength];
self.requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[self.requestOperation setOutputStream:[NSOutputStream outputStreamToFileAtPath:self.cachePath() append:NO]];
//处理流
[self readCacheToOutStreamWithPath:self.cachePath()];
[self.requestOperation addObserver:self forKeyPath:@"isPaused" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
//获取进度块
self.progressBlock = progressBlock;
//重组进度block
[self.requestOperation setDownloadProgressBlock:[self getNewProgressBlockWithCacheLength:cacheLength]];
//获取成功回调块
void (^newSuccess)(AFHTTPRequestOperation *operation, id responseObject) = ^(AFHTTPRequestOperation *operation, id responseObject){
success(operation,responseObject);
};
[self.requestOperation setCompletionBlockWithSuccess:newSuccess
failure:failure];
[self.requestOperation start];
}
#pragma mark - 获取本地缓存的字节
+(long long)cacheFileWithPath:(NSString*)path
{
NSFileHandle* fh = [NSFileHandle fileHandleForReadingAtPath:path];
NSData* contentData = [fh readDataToEndOfFile];
return contentData ? contentData.length : 0;
}
#pragma mark - 重组进度块
-(void(^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))getNewProgressBlockWithCacheLength:(long long)cachLength
{
typeof(self)newSelf = self;
void(^newProgressBlock)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) = ^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
{
NSData* data = [NSData dataWithContentsOfFile:self.cachePath()];
[self.requestOperation setValue:data forKey:@"responseData"];
// self.requestOperation.responseData = ;
newSelf.progressBlock(bytesRead,totalBytesRead + cachLength,totalBytesExpectedToRead + cachLength);
};
return newProgressBlock;
}
#pragma mark - 读取本地缓存入流
-(void)readCacheToOutStreamWithPath:(NSString*)path
{
NSFileHandle* fh = [NSFileHandle fileHandleForReadingAtPath:path];
NSData* currentData = [fh readDataToEndOfFile];
if (currentData.length) {
//打开流,写入data , 未打卡查看 streamCode = NSStreamStatusNotOpen
[self.requestOperation.outputStream open];
NSInteger bytesWritten;
NSInteger bytesWrittenSoFar;
NSInteger dataLength = [currentData length];
const uint8_t * dataBytes = [currentData bytes];
bytesWrittenSoFar = 0;
do {
bytesWritten = [self.requestOperation.outputStream write:&dataBytes[bytesWrittenSoFar] maxLength:dataLength - bytesWrittenSoFar];
assert(bytesWritten != 0);
if (bytesWritten == -1) {
break;
} else {
bytesWrittenSoFar += bytesWritten;
}
} while (bytesWrittenSoFar != dataLength);
}
}
#pragma mark - 获取请求
+(NSMutableURLRequest*)requestWithUrl:(id)url Range:(long long)length
{
NSURL* requestUrl = [url isKindOfClass:[NSURL class]] ? url : [NSURL URLWithString:url];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestUrl
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:5*60];
if (length) {
[request setValue:[NSString stringWithFormat:@"bytes=%lld-",length] forHTTPHeaderField:@"Range"];
}
return request;
}
#pragma mark - 监听暂停
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
//暂停状态
if ([keyPath isEqualToString:@"isPaused"] && [[change objectForKey:@"new"] intValue] == 1) {
long long cacheLength = [[self class] cacheFileWithPath:self.cachePath()];
//暂停读取data 从文件中获取到NSNumber
cacheLength = [[self.requestOperation.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue];
[self.requestOperation setValue:@"0" forKey:@"totalBytesRead"];
//重组进度block
[self.requestOperation setDownloadProgressBlock:[self getNewProgressBlockWithCacheLength:cacheLength]];
}
}
这就完成90%了,接下来就是调用了
定义一个DownLoadOperation属性operation(需要导入AFHTTPRequestOperationManager.h)
布局也不多说了哈,一个下载button,一个暂停button,一个显示进度的label,外加一个UIImageView
开始下载直接调用类里面的唯一的方法就行,代码如下:
- (IBAction)download:(id)sender
{
NSString* path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/temp"];
NSLog(@"path = %@",path);
operation = [[DownLoadOperation alloc] init];
[operation downloadWithUrl:Picture
cachePath:^NSString *{
return path;//存储路径
} progressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {//这个block写下载进度
float progress = (float)totalBytesRead / (float)totalBytesExpectedToRead;
[self.progressView setProgress:progress animated:YES];
[self.label setText:[NSString stringWithFormat:@"%.0f%%",progress * 100]];
UIImage* image = [UIImage imageWithData:operation.requestOperation.responseData];
[self.imageView setImage:image];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {//这是下载成功的回调
NSLog(@"success");
UIImage* image = [UIImage imageWithData:operation.responseData];
[self.imageView setImage:image];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {//这是下载失败的回调
NSLog(@"error = %@",error);
}];
}
//暂停
- (IBAction)pasuse:(id)sender
{
[operation.requestOperation pause];
}
//重新开始
- (IBAction)resume:(id)sender
{
[operation.requestOperation resume];
}
这就完成啦~
再扯点别的哈,本想写个oc从入门到精通的系列博客,发现真的没精力,所以就偷偷懒,写一些开发中常见和常用的东西吧~欢迎交流哈~~~
直接进入主题
新建一个类,继承自NSObject,我命名为DownLoadOperation
头文件重定义属性和方法如下
@property(nonatomic , strong) NSURL* url;
@property(nonatomic , copy) NSString* (^cachePath)(void);
@property(nonatomic , strong) AFHTTPRequestOperation* requestOperation;
@property(nonatomic , copy) void(^progressBlock)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead);
-(void)downloadWithUrl:(id)url
cachePath:(NSString* (^) (void))cacheBlock
progressBlock:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))progressBlock
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
接下来在.m中实现
-(void)downloadWithUrl:(id)url
cachePath:(NSString* (^) (void))cacheBlock
progressBlock:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))progressBlock
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
self.cachePath = cacheBlock;
//获取缓存的长度
long long cacheLength = [[self class] cacheFileWithPath:self.cachePath()];
//获取请求
NSMutableURLRequest* request = [[self class] requestWithUrl:url Range:cacheLength];
self.requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[self.requestOperation setOutputStream:[NSOutputStream outputStreamToFileAtPath:self.cachePath() append:NO]];
//处理流
[self readCacheToOutStreamWithPath:self.cachePath()];
[self.requestOperation addObserver:self forKeyPath:@"isPaused" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
//获取进度块
self.progressBlock = progressBlock;
//重组进度block
[self.requestOperation setDownloadProgressBlock:[self getNewProgressBlockWithCacheLength:cacheLength]];
//获取成功回调块
void (^newSuccess)(AFHTTPRequestOperation *operation, id responseObject) = ^(AFHTTPRequestOperation *operation, id responseObject){
success(operation,responseObject);
};
[self.requestOperation setCompletionBlockWithSuccess:newSuccess
failure:failure];
[self.requestOperation start];
}
#pragma mark - 获取本地缓存的字节
+(long long)cacheFileWithPath:(NSString*)path
{
NSFileHandle* fh = [NSFileHandle fileHandleForReadingAtPath:path];
NSData* contentData = [fh readDataToEndOfFile];
return contentData ? contentData.length : 0;
}
#pragma mark - 重组进度块
-(void(^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))getNewProgressBlockWithCacheLength:(long long)cachLength
{
typeof(self)newSelf = self;
void(^newProgressBlock)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) = ^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
{
NSData* data = [NSData dataWithContentsOfFile:self.cachePath()];
[self.requestOperation setValue:data forKey:@"responseData"];
// self.requestOperation.responseData = ;
newSelf.progressBlock(bytesRead,totalBytesRead + cachLength,totalBytesExpectedToRead + cachLength);
};
return newProgressBlock;
}
#pragma mark - 读取本地缓存入流
-(void)readCacheToOutStreamWithPath:(NSString*)path
{
NSFileHandle* fh = [NSFileHandle fileHandleForReadingAtPath:path];
NSData* currentData = [fh readDataToEndOfFile];
if (currentData.length) {
//打开流,写入data , 未打卡查看 streamCode = NSStreamStatusNotOpen
[self.requestOperation.outputStream open];
NSInteger bytesWritten;
NSInteger bytesWrittenSoFar;
NSInteger dataLength = [currentData length];
const uint8_t * dataBytes = [currentData bytes];
bytesWrittenSoFar = 0;
do {
bytesWritten = [self.requestOperation.outputStream write:&dataBytes[bytesWrittenSoFar] maxLength:dataLength - bytesWrittenSoFar];
assert(bytesWritten != 0);
if (bytesWritten == -1) {
break;
} else {
bytesWrittenSoFar += bytesWritten;
}
} while (bytesWrittenSoFar != dataLength);
}
}
#pragma mark - 获取请求
+(NSMutableURLRequest*)requestWithUrl:(id)url Range:(long long)length
{
NSURL* requestUrl = [url isKindOfClass:[NSURL class]] ? url : [NSURL URLWithString:url];
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestUrl
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:5*60];
if (length) {
[request setValue:[NSString stringWithFormat:@"bytes=%lld-",length] forHTTPHeaderField:@"Range"];
}
return request;
}
#pragma mark - 监听暂停
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
//暂停状态
if ([keyPath isEqualToString:@"isPaused"] && [[change objectForKey:@"new"] intValue] == 1) {
long long cacheLength = [[self class] cacheFileWithPath:self.cachePath()];
//暂停读取data 从文件中获取到NSNumber
cacheLength = [[self.requestOperation.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue];
[self.requestOperation setValue:@"0" forKey:@"totalBytesRead"];
//重组进度block
[self.requestOperation setDownloadProgressBlock:[self getNewProgressBlockWithCacheLength:cacheLength]];
}
}
这就完成90%了,接下来就是调用了
定义一个DownLoadOperation属性operation(需要导入AFHTTPRequestOperationManager.h)
布局也不多说了哈,一个下载button,一个暂停button,一个显示进度的label,外加一个UIImageView
开始下载直接调用类里面的唯一的方法就行,代码如下:
- (IBAction)download:(id)sender
{
NSString* path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/temp"];
NSLog(@"path = %@",path);
operation = [[DownLoadOperation alloc] init];
[operation downloadWithUrl:Picture
cachePath:^NSString *{
return path;//存储路径
} progressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {//这个block写下载进度
float progress = (float)totalBytesRead / (float)totalBytesExpectedToRead;
[self.progressView setProgress:progress animated:YES];
[self.label setText:[NSString stringWithFormat:@"%.0f%%",progress * 100]];
UIImage* image = [UIImage imageWithData:operation.requestOperation.responseData];
[self.imageView setImage:image];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {//这是下载成功的回调
NSLog(@"success");
UIImage* image = [UIImage imageWithData:operation.responseData];
[self.imageView setImage:image];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {//这是下载失败的回调
NSLog(@"error = %@",error);
}];
}
//暂停
- (IBAction)pasuse:(id)sender
{
[operation.requestOperation pause];
}
//重新开始
- (IBAction)resume:(id)sender
{
[operation.requestOperation resume];
}
这就完成啦~
再扯点别的哈,本想写个oc从入门到精通的系列博客,发现真的没精力,所以就偷偷懒,写一些开发中常见和常用的东西吧~欢迎交流哈~~~
相关文章推荐
- setObjectForKey和setValueForKey的用法和区别
- Jsp中request.getParameter("@param")值为[object HTMLInputElement]
- 通知中心(NSNotificationCenter)
- 论文笔记:Faster R-CNN:Towards Real-Time Object Detection with Region Proposal Networks
- RegExp Object
- 151220ObjectDemo
- Objective-C 内存管理机制
- objective-c 我的美图软件
- OObjective-c 二维码(第三方库ZXing)
- objective-c UIImagePickerController 相册视图控制器
- 【已解决】iPhone/iOS中保存自定义对象(Custom Object/Custom Class)的数组(NSMutableArray/NSArray)到NSUserDefaults
- IOS开发系列--Objective-C之KVC、KVO
- Objective-c:isKindOfClass 与 isMemberOfClass
- [Objective-C] id类型和instancetype类型
- Objective-c:类簇
- IOS开发基础Object-C( 14)-- 字符串的使用方法
- 【Objective-C学习记录】第三十四天
- Debugging Objects
- objective-c中实现函数重载(黑魔法:__attribute__((overloadable)))
- Object-c __attribute__((overloadable))) 用法