AFNetworking实现 断点续传
platform:ios,'7.0'
pod "AFNetworking","~> 2.3.0"
简单思路:通过重组progressBlock , successBlock ,requestUrl ,outPutStream,然后利用AFNetworking自带的pause,resume即可妥妥的实现。
不过碰到了一些问题也挺折磨人的,文件输入流,初始化就碰到问题了,
1.[self.requestOperation setOutputStream:[NSOutputStream outputStreamToFileAtPath:self.cachePath() append:YES]
如果追加了原始文件的data,就无法在下载过程中从outPutStream中获取到data。
[self.requestOperation setOutputStream:[NSOutputStream outputStreamToFileAtPath:self.cachePath() append:NO]
如果不追加,续传就成覆盖了。
官方文档也没仔细看,所幸就使用不追加的方式,然后手动同步文件与输入流。 方法如下
-(void)readCacheToOutStreamWithPath:(NSString*)path;
2.pause之后,AFHTTPRequestOperation.totalBytesRead,仍然记录这之前的读取长度,如果resume了,这是content-length发生了变法,这个时候就需要从组progressBlock了,同时也需要将totalBytesRead设为0,因为是私有属性,所以就用KVC。
#define Vedio @"http://221.228.249.82/youku/697A5CA0CEB3582FB91C4E3A88/03002001004E644FA2997704E9D2A7BA1E7B9D-6CAB-79A9-E635-3B92A92B3500.mp4"
#define Picture @"http://x1.zhuti.com/down/2012/11/29-win7/3D-1.jpg"
- (IBAction)download:(id)sender
{
NSString* path = [NSHomeDirectory()stringByAppendingPathComponent:@"Documents/temp"];
NSLog(@"path = %@",path);
operation = [[DownLoadOperationalloc] init];
[operationdownloadWithUrl:Picture
cachePath:^NSString *{
return path;
} progressBlock:^(NSUInteger bytesRead,long long totalBytesRead,long long totalBytesExpectedToRead) {
NSLog(@"bytesRead = %u ,totalBytesRead = %llu totalBytesExpectedToRead = %llu",bytesRead,totalBytesRead,totalBytesExpectedToRead);
float progress = totalBytesRead / (float)totalBytesExpectedToRead;
[self.progressViewsetProgress:progressanimated:YES];
[self.labelsetText:[NSStringstringWithFormat:@"%.2f%%",progress*100]];
UIImage* image = [UIImageimageWithData:operation.requestOperation.responseData];
[self.imageViewsetImage: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);
}];
}
#import <Foundation/Foundation.h>
#import "AFNetworking.h"
@interface DownLoadOperation :NSObject
@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 longtotalBytesExpectedToRead);
-(void)downloadWithUrl:(id)url
cachePath:(NSString* (^) (void))cacheBlock
progressBlock:(void (^)(NSUInteger bytesRead,long long totalBytesRead,long longtotalBytesExpectedToRead))progressBlock
success:(void (^)(AFHTTPRequestOperation *operation,id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation,NSError *error))failure;
@end
#import "DownLoadOperation.h"
@implementation DownLoadOperation
-(void)downloadWithUrl:(id)url
cachePath:(NSString* (^) (void))cacheBlock
progressBlock:(void (^)(NSUInteger bytesRead,long long totalBytesRead,long longtotalBytesExpectedToRead))progressBlock
success:(void (^)(AFHTTPRequestOperation *operation,id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation,NSError *error))failure
{
self.cachePath = cacheBlock;
//获取缓存的长度
longlong cacheLength = [[selfclass] cacheFileWithPath:self.cachePath()];
NSLog(@"cacheLength = %llu",cacheLength);
//获取请求
NSMutableURLRequest* request = [[selfclass] requestWithUrl:urlRange:cacheLength];
self.requestOperation = [[AFHTTPRequestOperationalloc] initWithRequest:request];
[self.requestOperationsetOutputStream:[NSOutputStreamoutputStreamToFileAtPath:self.cachePath()append:NO]];
//处理流
[selfreadCacheToOutStreamWithPath:self.cachePath()];
[self.requestOperationaddObserver:selfforKeyPath:@"isPaused"options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOldcontext:nil];
//获取进度块
self.progressBlock = progressBlock;
//重组进度block
[self.requestOperationsetDownloadProgressBlock:[selfgetNewProgressBlockWithCacheLength:cacheLength]];
//获取成功回调块
void (^newSuccess)(AFHTTPRequestOperation *operation,id responseObject) = ^(AFHTTPRequestOperation *operation,idresponseObject){
NSLog(@"responseHead = %@",[operation.responseallHeaderFields]);
success(operation,responseObject);
};
[self.requestOperationsetCompletionBlockWithSuccess:newSuccess
failure:failure];
[self.requestOperationstart];
}
#pragma mark - 获取本地缓存的字节
+(longlong)cacheFileWithPath:(NSString*)path
{
NSFileHandle* fh = [NSFileHandlefileHandleForReadingAtPath:path];
NSData* contentData = [fhreadDataToEndOfFile];
return contentData ? contentData.length :0;
}
#pragma mark - 重组进度块
-(void(^)(NSUInteger bytesRead,long long totalBytesRead,long longtotalBytesExpectedToRead))getNewProgressBlockWithCacheLength:(longlong)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 = [NSDatadataWithContentsOfFile:self.cachePath()];
[self.requestOperationsetValue:dataforKey:@"responseData"];
// self.requestOperation.responseData = ;
newSelf.progressBlock(bytesRead,totalBytesRead + cachLength,totalBytesExpectedToRead + cachLength);
};
return newProgressBlock;
}
#pragma mark - 读取本地缓存入流
-(void)readCacheToOutStreamWithPath:(NSString*)path
{
NSFileHandle* fh = [NSFileHandlefileHandleForReadingAtPath:path];
NSData* currentData = [fhreadDataToEndOfFile];
if (currentData.length) {
//打开流,写入data,未打卡查看 streamCode = NSStreamStatusNotOpen
[self.requestOperation.outputStreamopen];
NSInteger bytesWritten;
NSInteger bytesWrittenSoFar;
NSInteger dataLength = [currentDatalength];
constuint8_t * dataBytes = [currentDatabytes];
bytesWrittenSoFar = 0;
do {
bytesWritten = [self.requestOperation.outputStreamwrite:&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:(longlong)length
{
NSURL* requestUrl = [urlisKindOfClass:[NSURLclass]] ? url : [NSURLURLWithString:url];
NSMutableURLRequest* request = [NSMutableURLRequestrequestWithURL:requestUrl
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:5*60];
if (length) {
[request setValue:[NSStringstringWithFormat:@"bytes=%lld-",length]forHTTPHeaderField:@"Range"];
}
NSLog(@"request.head = %@",request.allHTTPHeaderFields);
return request;
}
#pragma mark - 监听暂停
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context
{
NSLog(@"keypath = %@ changeDic = %@",keyPath,change);
//暂停状态
if ([keyPathisEqualToString:@"isPaused"] && [[changeobjectForKey:@"new"]intValue] ==1) {
longlong cacheLength = [[selfclass] cacheFileWithPath:self.cachePath()];
//暂停读取data从文件中获取到NSNumber
cacheLength = [[self.requestOperation.outputStreampropertyForKey:NSStreamFileCurrentOffsetKey]unsignedLongLongValue];
NSLog(@"cacheLength = %lld",cacheLength);
[self.requestOperationsetValue:@"0"forKey:@"totalBytesRead"];
//重组进度block
[self.requestOperationsetDownloadProgressBlock:[selfgetNewProgressBlockWithCacheLength:cacheLength]];
}
}
@end