iOS 文件分段下载

1.使用工具类,提供实例方法供外部调用

#import <Foundation/Foundation.h>


@interface QKFileDownload : NSObject


- (void)downloadImageWithURL:(NSURL *)url;


@end


2.实现方法:  - (void)downloadImageWithURL:(NSURL *)url

2.1要实现文件的分段下载,得先知道欲下载文件的大小,然后把大小切割成N份

获取欲下载的文件的大小: 

long long netImageSize = [self netImageSizeWithURL:url];

实现私有方法 netImageSizeWithURL:url

// 计算网络图片的大小

- (long long) netImageSizeWithURL:(NSURL *)url

{

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:2.0f];

    request.HTTPMethod = @"HEAD";

    

    NSURLResponse *response = nil;

    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];

    

    NSLog(@"length = %lld",response.expectedContentLength);

    return response.expectedContentLength;

}

2.2完善  - ( void )downloadImageWithURL:( NSURL  *)url 方法

long long fromByte = 0;

        long long toByte = 0;

        while (netImageSize > kPerByte) {

            toByte = fromByte + (kPerByte - 1);

            

            [self downloadWithURL:url from:fromByte to:toByte];

            NSLog(@"%lld-%lld",fromByte,toByte);

            

    fromByte += kPerByte;

            netImageSize -= kPerByte

        }

        // 下载剩余的

        [self downloadWithURL:url from:fromByte to:fromByte + netImageSize - 1];

2.3实现  downloadWithURL :url  from :fromByte  to :toByte 方法

- (void) downloadWithURL:(NSURL *)url from:(long long)fromByte to:(long long)toByte

{

    // 确定request

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:1 timeoutInterval:2.0f];

    NSString *range = [NSString stringWithFormat:@"bytes=%lld-%lld",fromByte,toByte];

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

    // 返回每一段RangeData

    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL];

    // 追加数据

    [self append:data];

}

2.4.1 实现 append:data 方法

// 追加数据

- (void)append:(NSData *)data

{

    NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:self.cacheFile];

    if (!handle) { // 不存在cacheFile,创建文件

        [data writeToFile:self.cacheFile atomically:YES];

    }else{ // 存在cacheFile,追加数据

        [handle seekToEndOfFile];

        [handle writeData:data];

        [handle closeFile];

    }

}

2.4.2 实现cacheFile的get方法

- (NSString *)cacheFile

{

    if (!_cacheFile) {

        _cacheFile = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"test.png"];

    }

    return _cacheFile;

}

2.5 在  //  下载剩余的

      [self downloadWithURL:url from:fromByte to:fromByte + netImageSize - 1]; 的下面添加方法,告诉控制器图片下载好了,可以显示在控制器的self.imageView上了,方法:代理,通知,Block,这次用Block来实现,怎么设置Block,应该看外部的控制怎么用才"优雅".


2.6 打开 viewController.m

QKFileDownload *download = [[QKFileDownload alloc]init];

[download downloadImageWithURL:[NSURL URLWithString:@"http://localhost/123icon" 此处写Block];

初步的写法:[download downloadImageWithURL:[NSURL URLWithString:@"http://localhost/123icon" completion:(){ }]; // Block写在completion前的括号里

最终写法:

[download downloadImageWithURL:[NSURL URLWithString:@"http://localhost/123icon" completion:((void)(^)(UIImage *image)){ 

[self.imageView setImage:image];

}];

/*

注:Block的写法: 

(返回值)(^Block名字)(参数类型) = ^{

// 代码写在里面

}

想法:

(void)(^)(UIImage *image) = ^{

 // 代码写在这里面

}

*/

2.7.1 回到工具类.m文件,修改 - (void)downloadImageWithURL:(NSURL *)url 的方法名

改成:- (void)downloadImageWithURL:(NSURL *)url completion:((void)(^)(UIImage *image))completion{

long long netImageSize = [self netImageSizeWithURL:url];

……

}

2.7.2 回到工具类.h文件,修改方法名.回到viewController.m,再去敲Block,回车打开Block代码的方式出现了.

2.8.1 回到工具类.m文件,在 

        // 下载剩余的

        [self downloadWithURL:url from:fromByte to:fromByte + netImageSize - 1]; 的末尾,添加 completion(self.cacheImage);

2.8.2 实现cacheImage的get方法

- (UIImage *)cacheImage

{

    if (!_cacheImage) {

        _cacheImage = [UIImage imageWithContentsOfFile:self.cacheFile];

    }

    return _cacheImage;

}

2.9 如果本地缓存已经有请求数据了,如何避免再次向服务器要数据?

long long fromByte = 0;

  long long toByte = 0;  的前面添加 


        long long netImageSize = [self netImageSizeWithURL:url];

        long long localImageSize = [self localImageSizeWithURL:self.cacheFile];

        

        if (localImageSize == netImageSize) {

                completion(self.cacheImage);

            return;

        }

2.10 实现  localImageSizeWithURL : self . cacheFile 方法

- (long long)localImageSizeWithURL:(NSString *)str

{

    NSFileManager *mgr = [NSFileManager defaultManager];

    NSDictionary *dict = [mgr attributesOfItemAtPath:str error:NULL];

    return [dict[NSFileSize] longLongValue];

}

2.11 在c acheFile的get方法中,文件名被写死了,如何自动命名?

方案1:随机数命名(比如arc4random),会出现的情况:下载同一个文件,每次文件名都不一样,每次都要从服务器下载

方案2:根据时间戳命名,和方案1一样的问题

方案3:MD5命名,可以保证只要url路径没变,用MD5计算的名字就不会变,这样下载同一个文件,第2次下载时,localImageSize == netImageSize条件满足,程序就会从缓存中取

为NSString添加分类

- (NSString *)MD5

{

    const char *cStr = [self UTF8String];

    unsigned char digest[CC_MD5_DIGEST_LENGTH];

    CC_MD5(cStr, strlen(cStr), digest);

    NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];

    for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {

        [result appendFormat:@"%02x", digest[i]];

    }

    return result;

}

2.12.1 打开工具类.m文件的 downloadImageWithURL:( NSURL  *)url completion:((void)(^)(UIImage *image))completion 的首行位置

添加代码

self.cacheFile = [url absoluteString];

2.12.2 把cacheFile的get方法改成set方法

- (void) setCacheFile:(NSString *)url

{

    NSString *newFileName = [url MD5];

    NSString *fullFileName = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:newFileName];

    _cacheFile = fullFileName;

}

2.13 为文件下载添加多线程

就是把

- (void)downloadImageWithURL:(NSURL *)url completion:(void (^)(UIImage *image))completion里的所有内容塞到

    dispatch_queue_t q = dispatch_queue_create("com.ios.www", DISPATCH_QUEUE_SERIAL);

    dispatch_async(q, ^{

// 塞到这里

};

然后给把Block添加到主队列里,因为Block涉及到UI界面的刷新

            dispatch_async(dispatch_get_main_queue(), ^{

                completion(self.cacheImage);

            });








  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值