AFNetworking实现 断点续传

时间:2024-01-08 12:15:20
AFNetworking的版本:
platform:ios,'7.0'

pod "AFNetworking","~> 2.3.0"

AFNetworking实现  断点续传

AFNetworking实现  断点续传

AFNetworking实现  断点续传

简单思路:通过重组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]
如果不追加,续传就成覆盖了。AFNetworking实现  断点续传

官方文档也没仔细看,所幸就使用不追加的方式,然后手动同步文件与输入流。 方法如下

-(void)readCacheToOutStreamWithPath:(NSString*)path;

2.pause之后,AFHTTPRequestOperation.totalBytesRead,仍然记录这之前的读取长度,如果resume了,这是content-length发生了变法,这个时候就需要从组progressBlock了,同时也需要将totalBytesRead设为0,因为是私有属性,所以就用KVC。

[self.requestOperation setValue:@"0" forKey:@"totalBytesRead"];

------------随便贴个VC----------------------

- (IBAction)download:(id)sender

{

//http://202.102.88.133/youku/657114D0FE44481C592F964ABD/030020010053F4AB5FB92A01296A84C7E5A401-0FC6-BD65-4525-706B419E9EA6.mp4

//    http://b.hiphotos.baidu.com/image/h%3D1200%3Bcrop%3D0%2C0%2C1920%2C1200/sign=b284ea7541a98226a7c12f25bab28262/960a304e251f95ca8888fab6cb177f3e670952b4.jpg

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