网络下载文件封装

本文介绍了一个自定义的iOS下载组件,包括进度展示Button类及下载管理类的实现细节。通过绘图Button显示下载进度,并利用NSURLSession进行文件下载,支持断点续传功能。
1.定义一个绘图Button类来绘制下载进度
2.创建一个管理下载类

IB_DESIGNABLE  //定义在xib中会显示视图设置
@interface XJProgressButton : UIButton
/**
 * 
下载进度
 */

@property (nonatomic, assign)IBInspectable CGFloat progress;
/**
 * 
线条宽度
 */

@property (nonatomic, assign)IBInspectable CGFloat lineWidth;
/**
 * 
线条颜色
 */
@property (nonatomic, assign)IBInspectable UIColor *lineColor;

//buttom实现文件
#import "XJProgressButton.h"

@implementation XJProgressButton


- (
void)setProgress:(CGFloat)progress {

   
_progress = progress;
   
    [
self setNeedsDisplay]; //重新绘制
   
   
//设置标题
    [
self setTitle:[NSString stringWithFormat:@"%.2f%%",_progress *100] forState:UIControlStateNormal];

}


- (
void)drawRect:(CGRect)rect {

 
//按钮的宽
   
CGFloat btnW = rect.size.width;
   
CGFloat btnH = rect.size.height;
   
   
//1 圆的原点
   
CGPoint center = CGPointMake(btnW *0.5, btnH *0.5);
   
   
// 2.半径
   
CGFloat radius = MIN(btnW, btnH) *0.5 - self.lineWidth;
   
   
//3. 开始角度
   
CGFloat start = - M_PI_2;
   
   
//4 结束角度
    CGFloat end = M_PI *2 *self.progress +start;
   
    //贝塞尔绘图
   
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:start endAngle:end clockwise:YES];
   
   
//设置线宽
    path.
lineWidth = self.lineWidth;
   
   
//设置线的颜色
  
// [self.lineColor set];
   
   
//绘制
    [path stroke];
}

下载管理类.h
#import <Foundation/Foundation.h>
#import
<UIKit/UIKit.h>
@interface XJDownloadOperation : NSObject

/**
 * 
下载文件
 *
 * 
@param url           下载的URL
 * 
@param progressBlock 下载进度的回调
 * 
@param finished      下载完成的回调
 */

- (
void)downloadWithURL:(NSURL *)url progress:(void (^)(CGFloat progress))progressBlock finished:(void (^)(NSString *targtPath))finished;


/**
 * 
取消下载
 */
- (void)cancelDownload;

//下载管理类.m

#import "XJDownloadOperation.h"
#import
"MBProgressHUD+PKX.h"
@interface XJDownloadOperation ()

/**
 * 
下载文件的总进度
 */

@property (nonatomic, assign) long long contentLength;
/**
 * 
已经下载文件的总长度
 */

@property (nonatomic, assign) long long currentLength;

/**
 * 
输出流
 */

@property (nonatomic, strong) NSOutputStream *output;

/**
 * 
下载链接操作
 */

@property (nonatomic, strong) NSURLConnection *connection;
/**
 * 
文件保存的路径
 */


@property (nonatomic, copy) NSString *targetPath;
/**
 * 
下载进度的回调
 */

@property (nonatomic, copy) void (^progress)(CGFloat);


/**
 * 
下载完成回调
 */

@property (nonatomic, copy) void (^finished)(NSString *);
@end


@implementation XJDownloadOperation

- (
void)downloadWithURL:(NSURL *)url progress:(void (^)(CGFloat))progressBlock finished:(void (^)(NSString *))finished {

   
self.progress = progressBlock;
   
self.finished = finished;
   
//异步
   
dispatch_async(dispatch_get_global_queue(0, 0), ^{
       
       
//创建请求
       
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
       
        [
self checkServerFileSize:url];
       
       
//检查文件的大小
       
long long fileSize = [self checkLoactionFileSize];
       
       
       
//把已经下载的进度设置为文件的大小
       
self.currentLength = fileSize;

       
//判断是否要下载
       
if (fileSize == self.contentLength ) {
            [
MBProgressHUD showError:@"文件已经下载"];
           
NSLog(@"%@",self.targetPath);
        
           
//下载成功回到主线程告诉用户
           
dispatch_async(dispatch_get_main_queue(), ^{
               
               
self.finished(self.targetPath);
            });
           
           
//下载成功
           
if (self.progress) {
               
//设置下载进度为满
               
self.progress(1);
            }
           
           
return;
       
        }
       
       
//断点续传
        [request
setValue:[NSString stringWithFormat:@"bytes = %lld-",fileSize] forHTTPHeaderField:@"Range"];
       
       
//发送请求 设置代理
   
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
       
       
/** 如果runloop 没有输入源,或者timer,监听者,runloop 会停止
        //
如果有会直到任务完成
        //
调用run方法,会让runloop 跑起来,但是如果没有输入源,或者timer,监听者,会马上停止
       
        
如果子线程中使用下载的代理(或者过一会才执行的代理)timer,必须在子线程最后的地方调用run方法
        */

        [[
NSRunLoop currentRunLoop]run];
       
    });
}


/**
 *  
获取服务器文件的大小
 HEAD
专门用来下载之前获取文件信息(文件的大小)
 *
 * 
@param url
 */


- (
void)checkServerFileSize:(NSURL *)url {
   
//创建请求
   
NSMutableURLRequest *requst = [NSMutableURLRequest requestWithURL:url];
   
   
//获取文件信息的请求方法
    [requst
setHTTPMethod:@"HEAD"];
   
   
//接收到文件的响应
   
NSHTTPURLResponse *respose;
   
   
//发送请求 同步
    [
NSURLConnection sendSynchronousRequest:requst returningResponse:&respose error:nil];
   
   
   
//获取到的文件大小
   
self.contentLength = [respose.allHeaderFields[@"Content-Length"]longLongValue];
   
   
   
//拼接文件保存的路径
   
NSString *tmp = NSTemporaryDirectory();
   
NSLog(@"%@",tmp);
   
   
//建议保存的文件名
   
NSString *fileName = respose.suggestedFilename;
   
   
//文件保存的完整路径
   
self.targetPath = [tmp stringByAppendingPathComponent:fileName];
   
}




#pragma mark - ---------^^^^^^^^^ 检查本地文件的大小
- (long long )checkLoactionFileSize {
   
   
//查看本地文件的大小
   
NSFileManager *manager = [NSFileManager defaultManager];
   
//文件总大小
   
long long fileSize = 0;
   
   
//先判断文件是否存在
   
if (! [manager fileExistsAtPath:self.targetPath]) { //文件不存在 从新下载
        fileSize =
0;
       
    }
else {
       
       
//文件存在 判断关系
       
//取得的文件的属性
       
NSDictionary *fileAttr = [manager attributesOfItemAtPath:self.targetPath error:nil];
       
//本地文件的大小
       
long long locationSize = fileAttr.fileSize;
       
       
if (locationSize == self.contentLength) {
           
//下载完成
            fileSize = locationSize;
           
        }
else if (locationSize > self.contentLength){ //如果本地的大于下载
           
//下载出错 删除文件
            [manager
removeItemAtPath:self.targetPath error:nil];
           
           
//从新下载
            fileSize =
0;
        }
else if (locationSize < self.contentLength){ //本地比服务器的小
           
           
//断点续传
            fileSize = locationSize;
        }
    }
   
return fileSize;
}


#pragma mark - ---------^^^^^^^^^ 取消下载
- (void)cancelDownload {

   
//断点续传
    [
self.connection cancel];
   
   
//关闭文件操作
    [
self.output close];
}


#pragma mark - ---------^^^^^^^^^ 下载的代理方法
//接收到响应的调用
- (
void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
   
   
NSLog(@"接收到响应%@",response);
   
   
//防止没有设置初始化 出现问题
   
//self.currentLength = 0;
   
   
//把下载完成的数据写到沙盒路径中 或桌面
   
NSString *path = self.targetPath;
   
   
//输出流 不需要手动去创建文件
   
self.output = [NSOutputStream outputStreamToFileAtPath:path append:YES];
   
//先打开文件
    [
self.output open];
   
   
}

//接收到数据的下载
- (
void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
   
   
//接收到的总长度
   
self.currentLength += data.length;
   
   
//计算长度
   
CGFloat progress = self.currentLength *1.0 / self.contentLength;
   
NSLog(@"下载进度%.2f",progress);
   
NSLog(@"%@",[NSThread currentThread]);
   
   
//把二进制写入到文件
    [
self.output write:data.bytes maxLength:data.length];
   
   
//回调进度 调用比较频繁 所以使用子线程回调
   
if (self.progress) {
       
       
self.progress(progress);
    }

}


//下载完成的调用
- (
void)connectionDidFinishLoading:(NSURLConnection *)connection {
  
    [
MBProgressHUD showError:@"下载完成"];
   
//下载完成关闭文件
    [
self.output close];
   
// 因为完成加调,有可以完成之后马上会在主线程作操作,而且方法调用频率较低,不会影响到主线程的正常运行
   
dispatch_async(dispatch_get_main_queue(), ^{
   
       
self.finished(self.targetPath);
       
    });
  }

标题基于Python的自主学习系统后端设计与实现AI更换标题第1章引言介绍自主学习系统的研究背景、意义、现状以及本文的研究方法和创新点。1.1研究背景与意义阐述自主学习系统在教育技术领域的重要性和应用价值。1.2国内外研究现状分析国内外在自主学习系统后端技术方面的研究进展。1.3研究方法与创新点概述本文采用Python技术栈的设计方法和系统创新点。第2章相关理论与技术总结自主学习系统后端开发的相关理论和技术基础。2.1自主学习系统理论阐述自主学习系统的定义、特征和理论基础。2.2Python后端技术栈介绍DjangoFlask等Python后端框架及其适用场景。2.3数据库技术讨论关系型和非关系型数据库在系统中的应用方案。第3章系统设计与实现详细介绍自主学习系统后端的设计方案和实现过程。3.1系统架构设计提出基于微服务的系统架构设计方案。3.2核心模块设计详细说明用户管理、学习资源管理、进度跟踪等核心模块设计。3.3关键技术实现阐述个性化推荐算法、学习行为分析等关键技术的实现。第4章系统测试与评估对系统进行功能测试和性能评估。4.1测试环境与方法介绍测试环境配置和采用的测试方法。4.2功能测试结果展示各功能模块的测试结果和问题修复情况。4.3性能评估分析分析系统在高并发等场景下的性能表现。第5章结论与展望总结研究成果并提出未来改进方向。5.1研究结论概括系统设计的主要成果和技术创新。5.2未来展望指出系统局限性并提出后续优化方向。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值