当前位置: 首页 > 编程日记 > 正文

音乐(文件)断点下载

这篇文章介绍音乐等文件的下载,支持断点续传。

我们需要创建两个类
HYDownLoader:音乐下载的主类,可以进行新建下载、暂停下载、取消下载等。
HYFileTool:文件管理类,主要为HYDownLoader服务,可以判断文件是否存在、移动文件等。

一、文件管理工具类HYFileTool

HYFileTool类比较简单,直接上代码,.h文件已经把方法备注得比较清楚了
.h文件

#import <Foundation/Foundation.h>
@interface HYFileTool : NSObject/**判断文件是否存在@param filePath 文件路径@return 是否存在*/
+(BOOL)fileExists:(NSString *)filePath;/**获取文件大小@param filePath 文件路径@return 文件大小*/
+(long long)fileSize:(NSString *)filePath;/**移动文件到新的路径@param fromPath 文件的原路径@param toPath 文件的新路径*/
+(void)moveFile:(NSString *)fromPath toPath:(NSString *)toPath;/**删除文件@param filePath 文件路径*/
+(void)removeFile:(NSString*)filePath;@end

.m文件

#import "HYFileTool.h"@implementation HYFileTool+(BOOL)fileExists:(NSString *)filePath{if (filePath.length == 0) {}return [[NSFileManager defaultManager] fileExistsAtPath:filePath];
}+(long long)fileSize:(NSString *)filePath{if (![self fileExists:filePath]) {return 0;}NSDictionary *fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];return [fileInfo[NSFileSize] longLongValue];
}+(void)moveFile:(NSString *)fromPath toPath:(NSString *)toPath{if (![self fileExists:fromPath]) {return;}[[NSFileManager defaultManager] moveItemAtPath:fromPath toPath:toPath error:nil];
}+(void)removeFile:(NSString*)filePath{[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}@end

二、下载主类HYDownLoader.h
在HYDownLoader.h中先定义下载状态的枚举

typedef NS_ENUM(NSUInteger,HYDownloadState){HYDownloadStatePause,HYDownloadStateDowning,HYDownloadStateSuccess,HYDownloadStateFail
};

以及各种状态下的Block回调

typedef void(^DownLoadInfoType)(long long totalSize);
typedef void(^ProgressBlockType)(float progress);
typedef void(^SuccesswBlockType)(NSString *path);
typedef void(^FailBlockType)(void);
typedef void(^StateChangeBlockType)(HYDownloadState state);

然后定义对应的属性

/**下载状态*/
@property(nonatomic,assign,readonly) HYDownloadState state;/**下载进度*/
@property(nonatomic,assign,readonly) float progress;/**下载信息回调*/
@property(nonatomic,copy) DownLoadInfoType downLoadInfo;/**下载状态改变回调*/
@property(nonatomic,copy) StateChangeBlockType stateChangeInfo;/**下载进度改变回调*/
@property(nonatomic,copy) ProgressBlockType progressChange;/**下载成功回调*/
@property(nonatomic,copy) SuccesswBlockType successBlock;/**下载失败回调*/
@property(nonatomic,copy) FailBlockType failBlock;

以及方法

/**下载文件@param url 下载文件的网络地址*/
-(void)downLoader:(NSURL *)url;/**下载文件@param url 下载文件的网络地址@param downLoadInfo 下载信息block@param progressBlock 下载进度block@param successBlock 下载成功block@param failedBlock 下载失败block*/
-(void)downLoader:(NSURL *)url downLoadInfo:(DownLoadInfoType)downLoadInfo progress:(ProgressBlockType)progressBlock success:(SuccesswBlockType)successBlock failed:(FailBlockType)failedBlock;/**暂停当前任务*/
-(void)pauseCurrentTask;/**取消当前任务*/
-(void)cancelCurrentTask;/**取消并清除当前任务*/
-(void)cancelAndClean;

三、HYDownLoader.m中的变量

HYDownLoader.m中定义内部方法需要的变量

@interface HYDownLoader()<NSURLSessionDataDelegate>{long long _tempSize;//已下载文件大小long long _totalSize;//文件总大小
}@property (nonatomic,strong) NSURLSession *session;
@property (nonatomic,weak) NSURLSessionDataTask *dataTask;/**下载文件完成后的路径*/
@property (nonatomic,copy) NSString *downloadedPath;
/**下载文件时的路径*/
@property (nonatomic,copy) NSString *downloadingPath;
/**写入文件的流*/
@property (nonatomic,strong) NSOutputStream *outputStream;@end

对应的get和set方法

-(NSURLSession*)session{if (_session == nil) {NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];}return _session;
}-(void)setState:(HYDownloadState)state{if (_state == state) {return;}_state = state;if (self.stateChangeInfo) {self.stateChangeInfo(_state);}if (state == HYDownloadStateSuccess && self.successBlock) {self.successBlock(_downloadedPath);}if (state == HYDownloadStateFail && self.failBlock) {self.failBlock();}
}-(void)setProgress:(float)progress{_progress = progress;if (self.progressChange) {self.progressChange(progress);}
}

其中在setState中,我们根据不同的下载状态回调对应的Block通知外界。

四、HYDownLoader.m的主要下载方法downLoader:和downloadWithURL:offset:

-(void)downLoader:(NSURL *)url{//如果任务存在,当前只是暂停,则继续下载if ([url isEqual:self.dataTask.originalRequest.URL] && self.state == HYDownloadStatePause) {[self resumeCurrenttask];return;}//1.文件的存放//下载时存放到temp(此目录用于存放临时文件,app退出时会被清理)//下载完成后移动到cache(iTunes不会备份此目录,此目录下文件不会在app退出时删除)NSString *fileName = url.lastPathComponent;self.downloadedPath = [kCachePath stringByAppendingPathComponent:fileName];self.downloadingPath = [kTmpPath stringByAppendingPathComponent:fileName];//1.判断url地址对应的资源是否已下载完成//1.1如果已完成,则返回相关信息if([HYFileTool fileExists:self.downloadedPath]){NSLog(@"已下载完成(文件已存在)");self.state = HYDownloadStateSuccess;return;}[self downloadWithURL:url offset:0];//2.否则检查临时文件是否存在//2.1若存在,以当前已存在文件大小,作为开始字节请求资源。if ([HYFileTool fileExists:self.downloadingPath]) {//获取本地文件大小(已下载部分)_tempSize = [HYFileTool fileSize:self.downloadingPath];[self downloadWithURL:url offset:_tempSize];return;}// 本地大小 == 总大小 则移动到cache文件夹// 本地大小 > 总大小  则删除本地缓存,重新从0开始下载// 本地大小 < 总大小  从本地大小开始下载//2.2 不存在,则从0字节开始请求资源[self downloadWithURL:url offset:0];}

再写带有回调block的方法,此时只要给各个block赋值, 并调用上面下载的主方法:

-(void)downLoader:(NSURL *)url downLoadInfo:(DownLoadInfoType)downLoadInfo progress:(ProgressBlockType)progressBlock success:(SuccesswBlockType)successBlock failed:(FailBlockType)failedBlock{self.downLoadInfo = downLoadInfo;self.progressChange = progressBlock;self.successBlock = successBlock;self.failBlock  = failedBlock;[self downLoader:url];
}

downLoader:方法只是对当前状态做了判断,下载的核心其实是downloadWithURL:offset:方法。使用系统的NSURLSession进行网络请求。 HTTP1.1 协议(RFC2616)开始支持获取文件的部分内容,这为并行下载以及断点续传提供了技术支持。它通过在 Header 里两个参数实现的,客户端发请求时对应的是 Range ,服务器端响应时对应的是 Content-Range。所以断点续传的请求的关键在于,给request的请求头设置Range,表示只请求offset后面的数据。

/**根据开始字节,请求资源@param url 下载url@param offset 开始字节*/
- (void)downloadWithURL:(NSURL *)url offset:(long long)offset{NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:0];[request setValue:[NSString stringWithFormat:@"bytes=%lld-",offset] forHTTPHeaderField:@"Range"];self.dataTask = [self.session dataTaskWithRequest:request];[self resumeCurrenttask];
}

NSURLSession的请求回调为代理方式。HYDownLoader遵循NSURLSessionDataDelegate代理,并完成其中的三个代理回调:

#pragma mark - 协议//接收到响应头
- (void)URLSession:(NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveResponse:(nonnull NSHTTPURLResponse *)response completionHandler:(nonnull void (^)(NSURLSessionResponseDisposition))completionHandler{NSLog(@"%@",response);_totalSize = [response.allHeaderFields[@"Content-Length"] longLongValue];NSString *contentRangeStr = response.allHeaderFields[@"Content-Range"];if (contentRangeStr.length > 0) {_totalSize = [[[contentRangeStr componentsSeparatedByString:@"/"] lastObject] longLongValue];}self.downLoadInfo(_totalSize);if (_tempSize == _totalSize) {//文件移动到完成文件夹NSLog(@"下载完成,移动文件到完成文件夹");[HYFileTool moveFile:_downloadingPath toPath:_downloadedPath];completionHandler(NSURLSessionResponseCancel);self.state = HYDownloadStateSuccess;return;}if (_tempSize > _totalSize) {//删除临时缓存[HYFileTool removeFile:self.downloadingPath];//重新下载[self downLoader:response.URL];//取消请求completionHandler(NSURLSessionResponseCancel);}//继续接受数据self.state = HYDownloadStateDowning;self.outputStream = [NSOutputStream outputStreamToFileAtPath:self.downloadingPath append:YES];completionHandler(NSURLSessionResponseAllow);
}//继续接收数据
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{_tempSize += data.length;self.progress = 1.0 * _tempSize / _totalSize;[self.outputStream write:data.bytes maxLength:data.length];NSLog(@"接受数据");
}//请求结束
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{NSLog(@"接收完成");if (error == nil) {[HYFileTool moveFile:self.downloadingPath toPath:self.downloadedPath];self.state = HYDownloadStateSuccess;}else{if(error.code == -999){NSLog(@"取消下载");self.state = HYDownloadStatePause;}else{NSLog(@"下载错误%@",error);self.state = HYDownloadStateFail;}}[self.outputStream close];
}

其中的核心点我们详细解释下:

  1. 在didReceiveResponse接受到头信息后,通过completionHandler回调,决定对后续内容的操作,如取消请求或继续接收数据
  2. 通过outputStream流的方式写入文件。
  3. 文件接收中止后会回调didCompleteWithError:方法,这时候有三种情况:正常完成、用户取消、异常中止等,根据error可以分别判断。

完整代码:
https://github.com/dolacmeng/HYDownLoader

相关文章:

php设计模式

原型模式&#xff08;prototype&#xff09; PHP设计模式之&#xff1a;原型模式 刚刚了解了原型模式&#xff0c;通过资料以及自身了解整合以下资料&#xff1a; 原型模式通过复制已经存在的实例来返回新的实例&#xff0c;而不是新建实例&#xff0c;并且原型&#xff08;被复…

TextKit及应用

在iOS开发中我们常常使用UIKit的UITextView、UITextField、UILabel来显示文字。它们底层都是基于一个叫做TextKit的强大引擎。通过TextKit&#xff0c;我们可以方便地修改文字的样式和排版&#xff0c;而不需要直接操作复杂的Core Text。 1.什么是TextKit 在iOS7中&#xff0c…

判断类之间的父子关系

如何判断两个类之间的父子关系&#xff1f; java为我们提供了instanceof运算符&#xff0c;可以用来判断一个对象是否是否个类的实例&#xff0c;所以很容易的想到子类的对象肯定是父类的实例。但是如何所涉及到的类是不可实例化的该怎么办呢&#xff1f;好在java的Class为我们…

顺F速运,你被爱加M坑了

“ 顺F速运APP安全性分析。”之前的文章《Wireshark分析实战&#xff1a;某达速递登录帐号密码提取》对某达速递的APP进行了分析&#xff0c;该APP将数据完全暴露于网络流量中。于是对快递行业老大顺F速运的APP进行了分析&#xff0c;发现该APP质量还算上乘&#xff0c;但是&am…

[大数据之Spark]——Actions算子操作入门实例

Actions reduce(func) Aggregate the elements of the dataset using a function func (which takes two arguments and returns one). The function should be commutative and associative so that it can be computed correctly in parallel. 这个方法会传入两个参数&#x…

Runloop总结

1.什么是Runloop Runloop字面上翻译就是运行循环&#xff0c;也就是一直在转圈圈运行着&#xff0c;类似于do…while()。我们思考一个问题&#xff1a; 一个线程执行完成后就会退出&#xff0c;当我们启动一个iOS APP时&#xff0c;系统会调用main.m文件的main()函数: int m…

Android拷贝工程不覆盖原工程的配置方法

http://www.2cto.com/kf/201203/125131.html 在Eclipse中改包名的时候选择refactor-->rename,勾选Rename subpackages,这样就不需要一个个修改每个类中导入的包名了转载于:https://www.cnblogs.com/leihupqrst/p/3670224.html

顺F速运国际版,你的密码漏点了

“ 对顺F旗下各APP顺藤摸瓜分析——顺F速运国际版。”前文《顺F速运&#xff0c;你被爱加M坑了》提到&#xff0c;顺F速运APP使用爱加密加壳&#xff0c;流量中传输内容被加密并BASE64编码了&#xff0c;只是安全性不够&#xff0c;壳没有将顺丰的加密算法及密钥保护好。秉承避…

利用Injection插件加快Xcode编译速度

我们在调试iOS原生代码时&#xff0c;每次修改都需要CommandR来重新编译运行。当项目代码量很大&#xff0c;编译时间就会很漫长。因此对于开发中来说&#xff0c;如果能加快编译速度&#xff0c;能大大提高生产效率。如果我们能像Swift Playground、小程序或网页那样修改代码后…

存储过程的优缺点 (转载)

为什么要用存储过程 几个去 IBM 面试的兄弟回来抱怨&#xff1a;去了好几个不同的 IBM 项目组&#xff0c;几乎每个面试官问到数据库的时候都要问用没用过存储过程&#xff0c;烦人不&#xff1f;大家去面的程序员&#xff0c;又不是 DBA&#xff0c;以前的项目都没有用到存储…

计算机知识的学习

我不是计算机科班出生&#xff01; 大学里喜欢跟医电系的人混在一起&#xff0c;受到他们影响较多&#xff0c;开始喜欢上计算机&#xff01;win 98 Office 97 他们有的擅长C、有的擅长Flash、还有哥们喜欢硬件&#xff01; 西安的东六路是图书批发市场&#xff0c;我几乎每周…

Wireshark小技巧:将IP显示为域名

“ 本文介绍如何使Wireshark报文窗口的Source栏及Destination内的IP直接显示为域名&#xff0c;提升报文分析效率。” 一个典型的Wireshark界面如下&#xff1a; 从这个图里&#xff0c;能看到源IP及目的IP&#xff0c;在流量不大&#xff0c;数据不多的情况下&#xff0c;我…

个人学习某个系统或平台的3问式的整理和细化指引

i:三问&#xff1a;是什么&#xff1f;为什么&#xff1f;怎么样&#xff1f; ii:详细化问题指引&#xff1a;是什么的目的在于确定系统的大致范围&#xff0c;明确目标&#xff1a;->平台的主要功能是什么&#xff1f;业务流程是怎样的&#xff1f;业务范围有多大&#xff…

给iOS开发者的React Native入门使用教程

目录一. 原生iOS项目集成React Native二. 原生跳转RN页面三. 显示豆瓣热门电影列表四. 改为导航五.完整源代码一. 原生iOS项目集成React Native 创建一个新的文件夹&#xff0c;如RNProject&#xff0c;然后新建一个/ios的子文件夹&#xff0c;将已有的iOS项目全部文件复制进去…

PHP Memcached应用实现代码

肖理达 (KrazyNio AT hotmail.com), 2006.04. 06, 转载请注明出处 一、memcached 简介 在很多场合&#xff0c;我们都会听到 memcached 这个名字&#xff0c;但很多同学只是听过&#xff0c;并没有用过或实际了解过&#xff0c;只知道它是一个很不错的东东。这里简单介绍一下&a…

顺F分享,你是在裸奔吗?

“ 对顺F旗下各APP顺藤摸瓜分析——顺F分享。”前文对顺F速运和顺F速运国际版进行了分析&#xff0c;二者使用同一套接口&#xff0c;虽然保护强度不高&#xff0c;但对代码和数据的保护却区别对待&#xff0c;实在让人诧异。秉承避免浪费的原则&#xff0c;我们将持续对顺F旗下…

静态链接库与动态链接库 (二)动态链接库的编译与使用

上一篇文章里大概描述linux下静态链接库的编译与使用&#xff0c;下面讲动态链接库的编译与使用方法。 1. 什么是动态链接库 所谓动态链接库&#xff0c;是指编译的时候不会把程序引用到的库插入到执行程序里&#xff0c;而是在执行时候才会去加载相关的库&#xff0c;所有用到…

【React Native】react-navigation导航使用方法

目录集成react-navigation使用react-navigation上一篇介绍了如何在已有iOS项目中集成React Native。这一篇我们把上一篇的demo做下拓展&#xff0c;添加点击电影跳转到详情页。页面跳转使用React Native推荐的第三方导航控件&#xff1a;react-navigation 集成react-navigatio…

请说明在.net中常用的几种页面间传递参数的方法,并说出他们的优缺点。

QueryString 传递一个或多个安全性要求不高或是结构简单的数值。但是对于传递数组或对象的话&#xff0c;就不能用这个方法了 session(viewstate) 简单&#xff0c;但易丢失 作用于用户个人,过量的存储会导致服务器内存资源的耗尽。 application 对象的作用范围是整个全局&am…

邮Z速递物流,让用户密码在网络中遨游

“ 最近分析快递行业的APP上瘾了&#xff0c;求解救。”邮政作为快递行业一个傻大黑的存在&#xff0c;一直很奇怪&#xff0c;我一直在纳闷&#xff0c;邮政和EMS到底是不是一家&#xff0c;在很多网点&#xff0c;它们是一体的存在&#xff0c;但很多东西&#xff0c;又是各自…

servlet response 中文乱码

先&#xff0c;response返回有两种&#xff0c;一种是字节流outputstream&#xff0c;一种是字符流printwrite。 申明&#xff1a;这里为了方便起见&#xff0c;所有输出都统一用UTF-8编码。 先说字节流&#xff0c;要输出“中国"&#xff0c;给输出流的必须是转换为utf-8…

【React Native】iOS原生导航跳转RN页面

上一篇介绍了React Native使用react-navigation进行导航跳转页面&#xff0c;现在我们介绍下原生iOS中怎么导航进一个新的React Native页面。 一、原生跳转React Native 创建HYReactNativeManager管理类. 在HYReactNativeManager.h中声明实现声明RCTBridgeDelegate协议&…

mac 常用指令

苹果公司生产的Mac搭载OS x 系统&#xff0c;OS x基于Unix&#xff0c;所以很多指令都和linux大同小异。 以下是一些常用指令&#xff0c;一点点自己记录下&#xff0c;方便自己和他人。这篇文应该是长期更新的。 1.ls [选项] [目录名] 第一个当然是list 指令 列出目录的文件列…

如何高效地爬取链家的房源信息(一)

“Python实现的链家网站的爬虫第一部分。”在之前的文章&#xff0c;以链家成都站为例&#xff0c;分析过链家网站数据的爬取&#xff0c;文章如下&#xff1a;干货&#xff01;链家二手房数据抓取及内容解析要点但是&#xff0c;当时没有根据分析&#xff0c;将爬取实现。本系…

HDU5886 Tower Defence 【两遍树形dp】【最长链预处理】

题意&#xff1a;N个点的一棵带权树。切掉某条边的价值为切后两树直径中的最大值。求各个边切掉后的价值和&#xff08;共N-1项&#xff09;。 解法一&#xff1a; 强行两遍dp&#xff0c;思路繁琐&#xff0c;维护东西较多&#xff1a; dis表示以i为根的子树的直径&#xff0c…

NPOI读取Excel数据应用

NPOI 是 POI 项目的 .NET 版本。使用 NPOI 你就可以在没有安装 Office 或者相应环境的机器上对 WORD/EXCEL 文档进行读写。NPOI是构建在POI 3.x版本之上的&#xff0c;它可以在没有安装Office的情况下对Word/Excel文档进行读写操作。 需求&#xff1a;根据excel表格提供的SVN相…

pod setup慢的解决方法

最近使用pod setup更新CocoaPods本地检索库&#xff0c;无奈只有10几k&#xff0c;还中途报错。最终通过以下步骤&#xff0c;完成更新。 1.手动下载Specs检索库 执行pod setup后&#xff0c;实质是从github上clone检索库&#xff08;https://github.com/CocoaPods/Specs&…

如何高效地爬取链家的房源信息(二)

“Python实现的链家网站的爬虫第二部分。”本系列文将以链家南京站为例&#xff0c;使用Python实现链家二手房源信息的爬虫&#xff0c;将数据爬取&#xff0c;并存入数据库中&#xff0c;以便使用。本系列第一部分&#xff1a;如何高效地爬取链家的房源信息&#xff08;一&…

C#实现HttpPost提交文件

先建立一个WebApplication Web.config <?xml version"1.0" encoding"utf-8"?><configuration><system.web><!--<globalization requestEncoding"gb2312" responseEncoding"gb2312" fileEncoding"gb231…

16年10月18号2th运算符与流程结构

---恢复内容开始--- 2th: 一&#xff1a;运算符 算数运算符 - * / %取余 9%30 自增 --自减 关系运算符 < < > > 全等于 !不等于 逻辑运算符 & | &#xff01;非 ^异或 &&短路与 || 短路或 赋值…