iOS文件分段下载

来源:互联网 发布:淘宝靠谱俄罗斯代购店 编辑:程序博客网 时间:2024/05/21 17:12
 在Web开发中主要的请求方法有如下几种:
         GET请求:get是获取数据的意思,数据以明文在URL中传递,受限于URL长度,所以传输数据量比较小。
         POST请求:post是向服务器提交数据的意思,提交的数据以实际内容形式存放到消息头中进行传递,无法在浏览器url中查看到,大小没有限制。
        HEAD请求:请求头信息,并不返回请求数据体,而只返回请求头信息,常用用于在文件下载中取得文件大小、类型等信息。
 
 文件分段下载:

      实际开发文件下载的时候不管是通过代理方法还是静态方法执行请求和响应,我们都会分批请求数据,而不是一次性请求数据。假设一个文件有1G,那么只要每次请求1M的数据,请求1024次也就下载完了。那么如何让服务器每次只返回1M的数据呢?

     在网络开发中可以在请求的头文件中设置一个range信息,它代表请求数据的大小。通过这个字段配合服务器端可以精确的控制每次服务器响应的数据范围。例如指定bytes=0-1023,然后在服务器端解析Range信息,返回该文件的0到1023之间的数据的数据即可(共1024Byte)。这样,只要在每次发送请求控制这个头文件信息就可以做到分批请求。

     当然,为了让整个数据保持完整,每次请求的数据都需要逐步追加直到整个文件请求完成。但是如何知道整个文件的大小?可以通过头文件信息获取整个文件大小。但是这么做的话就必须请求整个数据,这样分段下载就没有任何意义了。所幸在WEB开发中我们还有另一种请求方法“HEAD”,通过这种请求服务器只会响应头信息,其他数据不会返回给客户端,这样一来整个数据的大小也就可以得到了.

下面直接看代码和运行效果:

#import "ViewController.h"#import "UIView+SHView.h"#define kFILE_BLOCK_SIZE (1024*5) //每次1KB#define ImageUrl  @"http://g.hiphotos.baidu.com/zhidao/pic/item/                  37d3d539b6003af33c9aba42362ac65c1038b6eb.jpg"@interface ViewController ()<NSURLConnectionDataDelegate>{        UITextField *_textField;    UIButton *_button;    UIProgressView *_progressView;    UILabel *_label;    UIImageView *_imageView;    long long _totalLength;    long long _loadedLength;}@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];     [self layoutUI];}//界面布局-(void)layoutUI{        //地址栏    _textField=[[UITextField alloc]initWithFrame:CGRectMake(10, 50, 300, 25)];    _textField.borderStyle=UITextBorderStyleRoundedRect;    _textField.textColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0];    _textField.text=@"beautiful.jpg";    [self.view addSubview:_textField];        //进度条    _progressView=[[UIProgressView alloc]initWithFrame:CGRectMake(_textField.left,_textField.bottom+10, 300, 25)];    [self.view addSubview:_progressView];        //状态显示    _label=[[UILabel alloc]initWithFrame:CGRectMake(_progressView.left, _progressView.bottom+20,_progressView.width, 25)];    _label.text=@"等待下载...";    _label.textColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0];    [self.view addSubview:_label];        //显示下载图片    _imageView=[[UIImageView alloc]initWithFrame:CGRectMake(_label.left,_label.bottom+20,_label.width,160)];    _imageView.backgroundColor=[UIColor grayColor];    [self.view addSubview:_imageView];        //下载按钮    _button=[[UIButton alloc]initWithFrame:CGRectMake(_imageView.left, _imageView.bottom+30,_imageView.width, 25)];    [_button setTitle:@"下载" forState:UIControlStateNormal];    [_button setTitleColor:[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0] forState:UIControlStateNormal];    [_button addTarget:self action:@selector(asyncDownloadFile) forControlEvents:UIControlEventTouchUpInside];    [self.view addSubview:_button];}// 异步下载文件-(void)asyncDownloadFile{    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        [self downloadFile];    });}// 文件下载-(void)downloadFile{        //下载图片的 url    _totalLength=[self getFileTotlaLength:ImageUrl];    _loadedLength=0;    long long startSize=0;    long long endSize=0;        //分段下载    while(startSize< _totalLength){        endSize=startSize+kFILE_BLOCK_SIZE-1;        if (endSize>_totalLength) {            endSize=_totalLength-1;        }        [self downloadFile:_textField.text startByte:startSize endByte:endSize];                //更新进度        _loadedLength+=(endSize-startSize)+1;        [self updateProgress];                        startSize+=kFILE_BLOCK_SIZE;            }}//取得文件大小-(long long)getFileTotlaLength:(NSString *)fileName{        //创建可变请求,缓存策略记得使用NSURLRequestReloadIgnoringCacheData清楚本地缓存,否则会一直下载原文件大小,只有清楚本地缓存,那么每次都会事先传到服务端大小的字段进行获取数据.    NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:[self getDownloadUrl]                     cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:5.0f];        //设置位头信息请求.所以返回信息就只有头信息了.    [request setHTTPMethod:@"HEAD"];        NSURLResponse *response;    NSError *error;    //注意这里使用了同步请求,直接将文件大小返回    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];    if (error) {        NSLog(@"detail error:%@",error.localizedDescription);    }    //取得内容长度.这是NSURLResponse类自带的属性,可以获取下载文件的大小.    return response.expectedContentLength;}//更新进度-(void)updateProgress{        //创建一个新的线程进行更新进度条    [[NSOperationQueue mainQueue] addOperationWithBlock:^{                if (_loadedLength==_totalLength) {                        _label.text=@"下载完成";            _imageView.image=[UIImage imageWithContentsOfFile:[self getSavePath:_textField.text]];                    }else{                        _label.text=@"正在下载...";        }        [_progressView setProgress:(double)_loadedLength/_totalLength];    }];}//每次下载指定块大小的数据-(void)downloadFile:(NSString *)fileName startByte:(long long)start endByte:(long long)end{        //设置请求的字段的范围    NSString *range=[NSString stringWithFormat:@"Bytes=%lld-%lld",start,end];    NSLog(@"%@",range);        NSMutableURLRequest *request= [NSMutableURLRequest requestWithURL:[self getDownloadUrl] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:5.0f];        //通过请求头设置数据请求范围.要对请求进行操作,肯定要对请求头做操作!    [request setValue:range forHTTPHeaderField:@"Range"];        NSURLResponse *response;    NSError *error;    //注意这里使用同步请求,避免文件块追加顺序错误    NSData *data= [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];    if(!error){        NSLog(@"dataLength=%lld",(long long)data.length);        [self fileAppend:[self getSavePath:fileName] data:data];    }    else{        NSLog(@"detail error:%@",error.localizedDescription);    }}//文件追加-(void)fileAppend:(NSString *)filePath data:(NSData *)data{        //以可写方式打开文件    NSFileHandle *fileHandle=[NSFileHandle fileHandleForWritingAtPath:filePath];        //如果存在文件则追加,否则创建    if (fileHandle) {                //移动指针到之前文件数据的末尾        [fileHandle seekToEndOfFile];        //末尾拼接数据        [fileHandle writeData:data];        [fileHandle closeFile];//关闭文件,一定要的            }else{                [data writeToFile:filePath atomically:YES];//创建文件    }}//取得请求链接-(NSURL *)getDownloadUrl{     NSString *urlStr=ImageUrl;    //URL中不能出现中文(file参数就可能是中文),需要对URL进行编码,否则会出错。    urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];    NSURL *url=[NSURL URLWithString:urlStr];        return url;}// 取得保存地址(保存在沙盒缓存目录)-(NSString *)getSavePath:(NSString *)fileName{        NSString *path=[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];    NSLog(@"保存在沙盒缓存目录 %@",[path stringByAppendingPathComponent:fileName]);    return [path stringByAppendingPathComponent:fileName];}@end
效果如下:

              

下载文件的生成过程:

              


0 0