关于断点续传的那些事

      最近在研究视频下载的内容,下载的时候需要实现断点续传,上网查了很多资料,大部分都是程序运行时可以实现断点续传,但是程序退出后,再次进入又得从头开始,所以研究了好几天,总结了以下几种能够实现程序重新运行时断点续传的方法,废话不多说,进入正题。

一共三种方法:(1)基于AFNetworking的AFDownloadRequestOperation(这个框架可以在gitHub上下载)。(2)NSURLConnection (3)AFNetworking

对于一个程序员,代码是最好的沟通工具,所以,直接上代码。

第一种方法:AFDownloadRequestOperation这个类已经为我们封装好了断点续传的方法,所以我们不不需要再设置。

首先在h文件中

 

@interface ViewController : UIViewController<ASIHTTPRequestDelegate,ASIProgressDelegate>

 

//显示进度的进度条

 

@property (weak, nonatomic) IBOutlet UIProgressView *myProgress;

 

//显示已下载文件和文件总大小的比例。

 

@property (weak, nonatomic) IBOutlet UILabel *sizeLabel;

 

//显示当前下载情况的label(单位:M)

 

@property (weak, nonatomic) IBOutlet UILabel *currentLabel;

 

//显示文件的总大小(单位:M)

 

@property (weak, nonatomic) IBOutlet UILabel *totalLabel;

 

@property (nonatomic, strong)ASIHTTPRequest * request;

 

@property (nonatomic, strong)AFDownloadRequestOperation *operation;

 

 

 

   // ********************************************************/

在viewController的m文件中

- (void)viewDidLoad {

    [super viewDidLoad];

        //创建下载文件的URL

    NSURL * url = [NSURL URLWithString:@"http://182.92.5.120/d/file/20150210/lrKXiNVNwfyns/1/mp4/1_1.mp4"];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:3600];

    //创建文件路径

    NSString * path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) lastObject];

    NSString * destionPath = [path stringByAppendingPathComponent:@"mp4"];

     self.operation = [[AFDownloadRequestOperation alloc] initWithRequest:request targetPath:destionPath shouldResume:YES];

//当下载成功后会执行的方法

    [self.operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

        NSLog(@"Successfully downloaded file to %@", destionPath);

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

        NSLog(@"Error: %@", error);

    }];

   __block ViewController * viewVC = self;(防止循环引用造成内存泄漏,所以用block修饰一下)

//当在下载的时候,会一直调用这个方法,只要是现在在进行,此方法就一直执行,所以一般显示进度,显示下载文件大小的代码都写在这个方法里面。

    [self.operation setProgressiveDownloadProgressBlock:^(AFDownloadRequestOperation *operation, NSInteger bytesRead, long long totalBytesRead, long long totalBytesExpected, long long totalBytesReadForFile, long long totalBytesExpectedToReadForFile) {

        float percentDone = totalBytesReadForFile/(float)totalBytesExpectedToReadForFile;

        viewVC.myProgress.progress = percentDone;

        viewVC.sizeLabel.text = [NSString stringWithFormat:@"%.0f%%",percentDone*100];

        viewVC.currentLabel.text= [NSString stringWithFormat:@"CUR : %lli M",totalBytesReadForFile/1024/1024];

        viewVC.totalLabel.text =  [NSString stringWithFormat:@"TOTAL : %lli M",totalBytesExpectedToReadForFile/1024/1024];

        NSLog(@"1");

    }];

 

- (IBAction)Start:(UIButton *)sender {

    [self.operation start];

}

AFDownloadRequestOperation 这个类本身封装方法已经写好,我们点用pause就是暂停,暂停后调用resume方法后就是断点续传,不虚设置其他的。

- (IBAction)Pause:(UIButton *)sender {

    [self.operation pause];

}

- (IBAction)goon:(UIButton *)sender {

    [self.operation resume];

}

 代码写到这里,一个简单的断点续传的小demo就写好了。

 

第二种方法:NSURLConnection

现在大部分开发者都不用NSURLConnection进行数据请求了,大部分都是在用af,大家都觉得NSURLConnection用起来特别麻烦,但是我一直都是在用NSURLConnection,觉得NSURLConnection虽然用法复杂,但是还是有一定优势的。直接上代码

 

首先在viewController的h文件中

@property (weak, nonatomic) IBOutlet UIProgressView *myProgress;

//下载的url

@property (nonatomic, strong)NSString * url;

//用来监听下载进度

@property (nonatomic, copy)void (^progressHandle)(double progress);

//用来监听下载完成

@property (nonatomic, copy)void (^completionHandler)();

//用来监听下载失败

@property(nonatomic,copy)void(^failureHandler)(NSError *error);

@property (nonatomic, copy)NSString * name;

//已下载文件的大小

@property (nonatomic, assign)long long currentLength;

//文件的总大小

@property (nonatomic, assign)long long sumLength;

//请求对象

@property (nonatomic, strong)NSURLConnection * connection;

//文件句柄

@property (nonatomic, strong)NSFileHandle * writeHandle;

 

 

在viewController的m文件中

- (void)start

{

    NSURL * url = [NSURL URLWithString:@"http://182.92.5.120/d/file/20150210/lrKXiNVNwfyns/1/mp4/1_1.mp4"];

    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];

    //设置请求头信息;

接下来的两句代码就就是为我们断点续传的关键,如果没有这两句是无法实现断点续传的。

    NSString *value=[NSString stringWithFormat:@"bytes=%lld-",self.currentLength];

    [request setValue:value forHTTPHeaderField:@"Range"];

    //发送请求(使用代理的方式)

    self.connection=[NSURLConnection connectionWithRequest:request delegate:self];

    [self.connection start];

    

}

 

NSUTLConnection的代理方法

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response

{

设置存放路径

    NSString * str = [@"http://182.92.5.120/d/file/20150210/lrKXiNVNwfyns/1/mp4/1_1.mp4" stringByReplacingOccurrencesOfString:@":" withString:@""];

    NSString * newStr = [str stringByReplacingOccurrencesOfString: @"/" withString:@""];

    NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

    NSString *filePath = [cache stringByAppendingPathComponent:newStr];

    NSFileManager * fielManage = [NSFileManager defaultManager];

    if (![fielManage fileExistsAtPath:filePath]) {

        [fielManage createFileAtPath:filePath contents:nil attributes:nil];

    }

    //拿到一个关于文件的handler句柄

    self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];

    //获取完整的文件长度

    self.sumLength = response.expectedContentLength;

}

 

只要是有接收数据此方法就会一直执行。

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data

{

        self.currentLength = self.currentLength + data.length;

        double progress = (double)self.currentLength/self.sumLength;

    self.myProgress.progress = progress;

        if (self.progressHandle) {sssssss

            self.progressHandle(progress);

        }

        [self.writeHandle seekToEndOfFile];

        [self.writeHandle writeData:data];

}

 这样,用NSURLCOnnection封装的下载器就可以用了,这是第二种方法。

 

 

                                                             第三种方法 AFNetworking

 还是直接上代码,在viewController的h文件中

@interface ViewController : UIViewController

用来显示进度的进度条

@property (weak, nonatomic) IBOutlet UIProgressView *myProgress;

请求数据对象

@property (nonatomic, strong)AFHTTPRequestOperation * operation;

@end

 

在viewController的m文件中。

 

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

    

    

}

 此方法用来返回需要需要下载的文件的大小

- (unsigned long long)fileSizeForPath:(NSString *)path {

    signed long long fileSize = 0;

    NSFileManager *fileManager = [NSFileManager new]; // 用defalut线程不安全

        NSData * data = [fileManager contentsAtPath:path];

        NSInteger length = [data length];

       return length;

}

开始下载

- (void)startDownload {

    NSString *downloadUrl = @"http://182.92.5.120/d/file/20150210/lrKXiNVNwfyns/1/mp4/1_1.mp4";

    NSString *cacheDirectory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];

    NSString *downloadPath = [cacheDirectory stringByAppendingPathComponent:@"mp3"];

    NSLog(@"%@",downloadPath);

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadUrl]];

    //检查文件是否已经下载了一部分

    unsigned long long downloadedBytes = 0;

    if ([[NSFileManager defaultManager] fileExistsAtPath:downloadPath]) {

        //获取已下载的文件长度

        downloadedBytes = [self fileSizeForPath:downloadPath];

        if (downloadedBytes > 0) {

            NSMutableURLRequest *mutableURLRequest = [request mutableCopy];

            NSString *requestRange = [NSString stringWithFormat:@"bytes=%llu-", downloadedBytes];

            [mutableURLRequest setValue:requestRange forHTTPHeaderField:@"Range"];

            request = mutableURLRequest;

        }

    }

    //不使用缓存,避免断点续传出现问题

    [[NSURLCache sharedURLCache] removeCachedResponseForRequest:request];

    //下载请求

    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

    //下载路径

    operation.outputStream = [NSOutputStream outputStreamToFileAtPath:downloadPath append:YES];

    //下载进度回调

    [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {

        //下载进度

        float progress = ((float)totalBytesRead + downloadedBytes) / (totalBytesExpectedToRead + downloadedBytes);

        self.myProgress.progress = progress;

        NSLog(@"1");

    }];

    //成功和失败回调

    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

        

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

        

    }];

    [operation start];

 

   self.operation = operation;

}

 

- (IBAction)Start:(UIButton *)sender {

    [self startDownload];

}

- (IBAction)goon:(UIButton *)sender {

    [self.operation resume];

}

- (IBAction)Pause:(UIButton *)sender {

    [self.operation pause];

}

 这样,断点续传就实现了。

这三种方法都是可以实现断点续传的,如果哪里不对,还望大神批评指正。

 

posted on 2015-05-25 20:01  键盘上的舞者LQB  阅读(231)  评论(0编辑  收藏  举报

导航