​​用AFNetworking实现断点续传,暂停,继续​​

  ​​AFNetworking​​​​断点续传​​​​暂停​​​​恢复​

AFNetworking的版本:

platform:ios,'7.0'

pod "AFNetworking","~> 2.3.0"

 

AFNetworking实现  断点续传_#pragma

 

AFNetworking实现  断点续传_iOS 网络_02

AFNetworking实现  断点续传_#import_03

 

简单思路:通过重组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实现  断点续传_#import_04 

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

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

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

          [self.requestOperation setValue:@"0" forKey:@"totalBytesRead"];     ------------随便贴个VC----------------------  

#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

{

//​​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