网络编程归纳总结七阶段

网络编程归纳总结七阶段
断点续传

文件下载的时候不能像上一个练习中一样,文件存在不能直接删除文件。应该做一些决策
判断本地文件,如果本地文件存在要判断文件的大小
如果没有本地文件,下载
如果本地文件存在,发送Head请求获取服务器文件大小
本地文件大小==服务器文件大小,不下载
本地文件大小< 服务器文件大小,从之前的位置开始下载
本地文件大小> 服务器文件大小,删除本地文件,从新下载

检查服务器文件
//获取服务器上文件的信息(文件名和文件大小)
- (void)checkServerInfo:(NSURL *)url{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"head";
    NSURLResponse *response = nil;
    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];
    //通过属性记录文件的大小,拼接要保持的文件的路径
    self.expectedContentLength = response.expectedContentLength;
    self.targetPath = [NSTemporaryDirectory() stringByAppendingPathComponent:response.suggestedFilename];
}
检查本地文件
//如果没有文件  返回0  从头下载
//如果文件的大小>服务器文件大小  返回0 删除文件 从头下载
//如果文件的大小=服务器文件大小  返回文件大小  如果大小下等,不用再次下载
//如果文件的大小<服务器文件大小  返回文件大小
- (long long)checkLocalInfo{
    NSFileManager *manager = [NSFileManager defaultManager];
    long long fileSize = 0;
    //检查是否有文件
    if ([manager fileExistsAtPath:self.targetPath]) {
        //获取文件大小
        NSDictionary *attrsDic = [manager attributesOfItemAtPath:self.targetPath error:NULL];
        fileSize = attrsDic.fileSize;
        //获取文件大小
//        NSLog(@"%lld",[attrsDic[NSFileSize] longLongValue]);
//        NSLog(@"%lld",attrsDic.fileSize);
    }
    if (fileSize > self.expectedContentLength) {
        //如果本地的文件大小。比服务器的大。删除
        [manager removeItemAtPath:self.targetPath error:NULL];
        fileSize = 0;
    }
    return fileSize;
}
下载

如果本地文件和服务器文件大小相等,不下载

//检查本地文件
    long long fileSize = [self checkLocalInfo];
    NSLog(@"%lld",fileSize);
    if (fileSize == self.expectedContentLength) {
        NSLog(@"已经下载");
        return;
    }

从指定偏移处开始下载

//从指定偏移出下载文件
- (void)downloadFile:(NSURL *)url offset:(long long)offset{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:1 timeoutInterval:15];
    self.currentFileSize = offset;
    //range的取值
    //bytes=x-y  从x字节开始下载,下载到y字节
    //bytes=x-   从x字节开始下载,直到最后
    //bytes=-x   从0字节开始下载,下载到x字节
//range: bytes=x-y
    NSString *rangeStr = [NSString stringWithFormat:@"bytes=%lld-",offset];
    [request setValue:rangeStr forHTTPHeaderField:@"range"];
    //设置connection的代理
    NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
    //开始
    [conn start];
}
异步下载

默认代理的方法都在主线程上执行,下载会卡死

异步下载
NSURLConnection的代理方法,想在子线程上执行的话必须开启消息循环
把下载方法中的所有代码都放在异步队列中执行
- (void)download:(NSString *)urlStr
[[NSOperationQueue new] addOperationWithBlock:^{}]
在指定位置处开启消息循环,消息循环的模式必须是default模式
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
       //启动消息循环
       [[NSRunLoop currentRunLoop] run];
下载完成后的回调

下载完成或出错之后要在主界面做提示,现在所有的下载操作都封装在Downloader这个类中学习SDWebImage中的做法,给下载操作传入需要的block,当下载完成或出错的时候调用
修改Downloader头文件中的下载方法,增加需要的block,进度、完成、出错的block
修改Downloader.m中的实现方法,因为具体的进度、完成、出错都是在URLConnection的代理方法实现,所以传入block后需要定义属性接收

对应的位置调用回调方法

//URLConnection的下载方法中调用进度的回调,在当前子线程中执行
if (self.progressBlock) {
        self.progressBlock(progress);
    }

URLConnection的下载完成的方法中调用完成的回调,下载完成会回归主线程调用,在主线程上更新界面

if (self.successBlock) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.successBlock(self.targetPath);
        });
    }           

URLConnection的下载出错的方法中调用出错的回调,在哪个线程执行由调用者决定

if (self.errorBlock) {
        self.errorBlock(error);
    }

如果文件已经在,也要调用完成的回调
controller中调用

下载进度提示

界面上放置一个自定义按钮,设置大小
创建按钮的自定义类(按钮必须是custom的)
定义一个progress的属性,把进度传过来
每当给progress属性赋值的时候调用setNeedsDisplay重绘

- (void)setProgress:(CGFloat)progress{
    _progress = progress;
    [self setTitle:[NSString stringWithFormat:@"%0.2f%%",progress*100] forState:UIControlStateNormal];
    [self setNeedsDisplay];
}
画进度

重写drawRect方法,根据progress画圆

- (void)drawRect:(CGRect)rect {
    UIBezierPath *path = [UIBezierPath bezierPath];
    CGSize size = self.frame.size;
    //原点
    CGPoint center = CGPointMake(size.width/2, size.height/2);
    //半径
    CGFloat radius = (MIN(size.width, size.height) - 5)/2;
    //起始位置  从最上面开始画
    CGFloat startAngle = -M_PI_2;
    //结束位置
    CGFloat endAngle = 2*M_PI*self.progress+startAngle;

    [path addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];

    path.lineWidth = 5;
    path.lineCapStyle = kCGLineCapRound;
    [[UIColor orangeColor] setStroke];
    [path stroke];
}
暂停下载
//暂停下载,暂停下载就是调用connection的cancel方法
- (void)pause{
    [self.conn cancel];
}
下载管理类和缓存池

示例代码:

//创建下载的管理类HMDownloaderManager,单例
+ (instancetype)defaultManager{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

下载,调用下载器的下载方法
定义缓存池,当开始下载,把下载器缓存起来
下载之前先判断缓存池中是否有下载操作
下载完成或失败后,从缓存池移除下载操作—–最终解决重复下载的问题

下载管理类
- (void)download:(NSString *)urlStr progress:(void (^)(float))progress success:(void (^)(NSString *))success error:(void (^)(NSError *))error{
    //创建下载器之前。先判断针对该文件是否有下载操作
    if ([self.downloadCache objectForKey:urlStr] != nil) {
        NSLog(@"正在下载");
        return;
    }

    //1 下载器
    HMDownloader *downloader = [[HMDownloader alloc] init];

    //2 把下载器添加到缓存池
    [self.downloadCache setObject:downloader forKey:urlStr];

    //3 下载
    [downloader download:urlStr progress:progress success:^(NSString *targetPath) {
        [self.downloadCache removeObjectForKey:urlStr];
        //下载完成
        if (success != nil) {
            success(targetPath);
        }
    } error:^(NSError *e) {
        [self.downloadCache removeObjectForKey:urlStr];
        //出错
        if (error != nil) {
            error(e);
        }
    }];
}
Downloader改成NSOperation
+ (instancetype)downloader:(NSString *)urlStr progress:(void (^)(float))progress success:(void (^)(NSString *))success error:(void (^)(NSError *))error{
    HMDownloader *downloader = [[HMDownloader alloc] init];
    downloader.progressBlock = progress;
    downloader.successBlock = success;
    downloader.errorBlock = error;
    downloader.urlStr = urlStr;
    return downloader;
}
- (void)main{
    @autoreleasepool {
        NSURL *url = [NSURL URLWithString:str];
        //1 检查服务器文件信息
        [self checkServerInfo:url];
         //2 检查本地文件
        long long fileSize = [self checkLocalInfo];
        //    NSLog(@"%lld",fileSize);
        if (fileSize == self.expectedContentLength) {
            NSLog(@"已经下载");
            if (self.successBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    self.successBlock(self.targetPath);
                });
            }
            return;
        }
        //3 根据本地文件的长度 从对应偏移位置开始下载
        [self downloadFile:url offset:fileSize];
    }
}
取消下载操作
- (void)pause:(NSString *)urlStr{
    if ([self.downloadCache objectForKey:urlStr] == nil) {
        NSLog(@"没有此下载操作");
        return;
    }
    JSDownloader *downloader = [self.downloadCache objectForKey:urlStr];

    //暂停 connection
    [downloader pause];
    //取消操作
    [downloader cancel];
    //把操作从缓存池中移除
    [self.downloadCache removeObjectForKey:urlStr];
}

下载操作中还要取消正在执行的操作
//取消正在执行的操作
        if (self.isCancelled) {
            return;
        }

最近才开始往github上放东西 在公司写的又不能放= = 大家姑且看看吧

github地址: https://github.com/FuThD

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值